我使用LazyReference类已经有几年了(当然不是定期使用,但是有时它非常有用)。可以在here中看到该类。归功于Robbie Vanbrabant(班级作家)和Joshua Bloch,他着名的“ Effective Java 2 edt”。 (原始代码)。

该类正常工作(在Java 5+中),但是存在一个小潜在问题。如果instanceProvider返回null(嗯,它一定不是根据Guice Provider.get()合同,而是…),则在每次执行LazyReference.get()方法时,将保留LOCK,并反复调用instanceProvider.get。对于那些违反合同的人来说,这似乎是一种很好的惩罚(呵呵),但是如果真的需要懒惰地初始化一个具有设置null值的字段的方法怎么办?

我对LazyReference进行了一些修改:

public class LazyReference<T> {

  private final Object LOCK = new Object();

  private volatile T instance;

  private volatile boolean isNull;

  private final Provider<T> instanceProvider;

  private LazyReference(Provider<T> instanceProvider) {
    this.instanceProvider = instanceProvider;
  }

  public T get() {
    T result = instance;
    if (result == null && !isNull) {
      synchronized (LOCK) {
        result = instance;
        if (result == null && !isNull) {
          instance = result = instanceProvider.get();
          isNull = (result == null);
        }
      }
    }
    return result;
  }
}


恕我直言,它应该可以正常工作(如果您有其他意见,请发表您的评论和批评)。但是我不知道如果从volatile布尔值中删除isNull修饰符(当然,将其保留为instance)会发生什么情况?它仍然可以正常工作吗?

最佳答案

正如Neil Coffey所指出的,此代码包含一个竞争条件,但可以很容易地按以下方式解决(请注意,instance不必是volatile):

public class LazyReference<T> {
  private T instance;
  private volatile boolean initialized;
  ...
  public T get() {
    if (!initialized) {
      synchronized (LOCK) {
        if (!initialized) {
          instance = instanceProvider.get();
          initialized = true;
        }
      }
    }
    return instance;
  }
}

08-07 01:08