我已经使用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
)?它还能正常工作吗?
答案 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;
}
}