我使用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;
}
}