具有双重检查锁定和空值处理的LazyReference

时间:2011-06-22 18:37:36

标签: java null volatile lazy-initialization double-checked-locking

我已经使用LazyReference课程几年了(当然不是经常学习,但有时它非常有用)。可以看到班级here。积分给Robbie Vanbrabant(班级作者)和Joshua Bloch着名的“Effective Java 2nd edt”。 (原始代码)。

该类正常工作(在Java 5+中),但有一个潜在的小问题。如果instanceProvider返回null(好吧,它不能根据Guice Provider.get()合同,但是......)然后在每次执行LazyReference.get()方法时,LOCK将被保留并{{1将被一遍又一遍地调用。对于违反合同的人来说,这似乎是一个很好的惩罚(他 - 他),但如果真的需要懒惰地初始化一个可能设置instanceProvider.get值的字段呢?

我稍微修改了一下LazyReference:

null

恕我直言,它应该工作得很好(如果你有另一个意见,请发表你的意见和批评)。但是我想知道如果我从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)?它还能正常工作吗?

2 个答案:

答案 0 :(得分:3)

上面的代码有一个竞争条件:在设置了isNull之前,instance可以从instanceProvider.get()的结果设置为“real”null。

你确定要废弃这个复杂的废话并正确同步你会不会更容易。我敢打赌,你将无法衡量任何性能差异,并且更容易验证你的代码是否正确。

答案 1 :(得分:2)

正如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;
  }
}