最终以非阻塞线程安全方式加载成员变量值

时间:2016-04-30 10:03:09

标签: java multithreading oop design-patterns concurrency

我有一个有getter方法的类(等等):

public class Employee {  

  public EmployeeAccount getAccount() {  
     return this.empAccount;  
  }  
}  

问题是empAccount是由异步调用初始化的,可能会返回null但最终会返回实际帐户。
原因是异步调用方法依赖于很多东西,有时可能会返回null,因为它尚未准备好提供帐户。请注意,我无法控制此API 所以我想做的事情如下:

public class Employee {  
   public EmployeeAccount getAccount() {  
      if(this.empAccount != null) {   
         retrieveAccount(); 
      }
       return this.empAccount; 
   }
   private void retrieveAccount() {  
      Thread t = new Thread(new Runnable() {  
          @Override   
          public void run() {  
             this.empAccount = getAccountFromRemoteSystem(); // <--- this is a blocking call   
          }  
      };  
     t.start();  
}  

我的目标是因为getAccount()是从UI线程调用的非阻塞。 我如何设计/构造我的代码,以便它是线程安全的?是否有更好的构造我可以使用或其他一些设计模式?

1 个答案:

答案 0 :(得分:0)

答案会产生规范问题。您是否指定Employee类的实例可用,即使该实例的EmployeeAccount(您应该将其重命名为Account)是还没有?

如果帐户尚未设置,则它似乎是有效的Employee实例。在这种情况下,重要的是在getAccount方法的合同中声明:

/** Returns the {@linkplain EmployeeAccount} with this instance. 
    It may be null, if the account is not yet set up at the time of 
    this call. Typically, the clients should retry if this method     
    returns null.

    @return EmployeeAccount if it is available, null otherwise.
*/
public EmployeeAccount getAccount() {
    //
}

account的构造推迟到以后的时间会导致帐户永远不会为有效的Employee引用分配有效的非空值。这就是为什么没有final字段和不可变实例的原因。

如果这个警告是你和你的客户可以接受的,那么我将继续你的上述事情。确保employee字段为volatileAtomicReference,以便后台线程从远程/异步调用中检索真实帐户的更新是可见到其他线程(例如调用getter的UI线程)。