如何解决Java中的“双重检查锁定已损坏”声明?
我想为Java中的多线程实现延迟初始化。
我有一些这样的代码:
class Foo { private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
Helper h;
synchronized(this) {
h = helper;
if (h == null)
synchronized (this) {
h = new Helper();
} // release inner synchronization lock
helper = h;
}
}
return helper;
}
// other functions and members...
}
我得到了“双重检查锁定已损坏”声明。
我该如何解决?
回答:
以下是第71项中建议的惯用语:明智地使用 Effective Java:
如果你需要使用延迟初始化来提高实例字段的性能,请使用double-check idiom。这种习惯用法避免了在初始化字段后访问字段时发生锁定的费用(项67)。习惯用法的想法是检查字段的值两次(因此,将其命名为double-check):一次不锁定,然后,如果该字段似乎未初始化,则第二次锁定。仅当第二次检查表明该字段未初始化时,该调用才会初始化该字段。因为如果字段已经初始化就没有锁定,所以声明该字段至关重要volatile(项目66)。这是成语:
// Double-check idiom for lazy initialization of instance fieldsprivate volatile FieldType field;
private FieldType getField() {
FieldType result = field;
if (result != null) // First check (no locking)
return result;
synchronized(this) {
if (field == null) // Second check (with locking)
field = computeFieldValue();
return field;
}
}
此代码可能看起来有些混乱。特别是,对于局部变量结果的需求可能不清楚。此变量的作用是确保在已初始化字段的常见情况下,该字段仅被读取一次。尽管不是绝对必要的,但是这可以提高性能,并且通过应用于低级并发编程的标准可以更加优雅。在我的机器上,上述方法比不带局部变量的明显方法快25%。
在1.5版之前,由于volatile修饰符的语义不足以支持它,所以双重检查惯用语不能可靠地工作[Pugh01]。版本1.5中引入的内存模型解决了此问题[JLS,17,Goetz06 16]。今天,仔细检查惯用语是延迟初始化实例字段的一种选择技术。虽然你也可以将双重检查惯用法应用于静态字段,但没有理由这样做:惰性初始化持有人类惯用法是更好的选择。
参考
- 有效的Java,第二版
- 项目71:明智地使用延迟初始化
以上是 如何解决Java中的“双重检查锁定已损坏”声明? 的全部内容, 来源链接: utcz.com/qa/405084.html