如何解决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 fields

private 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

回到顶部