DCL还坏吗?

Is DCL still broken?

据我对旧 JMM 的了解,实现惰性单例的 DCL(双重检查锁定)技巧已被破坏,但我认为它已通过新 JMM 和易失性字段修复...

但是在 this 一篇不错的文章中,它显然是新的,足以引用新旧 JMM 和 DCL 中的 volatile 字段,指出它仍然是坏的...

我在这里和那里看到它是固定的然后我发现了这个...有人能说最后它坏了吗?

我的理解是,通过 volatile 保证关系之前发生的事情并有效地发布一个 membar 解决了问题,DCL 现在有效...虽然我同意 static lazy init 更可取并且更容易理解...

已在 Java 5.

中修复

然而现在 "correct"(即最简单的)方法是使用枚举进行延迟初始化。

public enum Singleton {
    INSTANCE;

    // No need for a getInstance() method
    //public static Singleton getInstance() {
    //    return INSTANCE;
    //}
    // Add methods to your liking
}

Here and there i read that it is fixed then i discover this... Can someone just say finally is it broken or not?

这取决于你所说的 "it" 是什么意思。

如果你问是否可以使用 volatile 做 DCL,那么答案是肯定的,post Java 5.(volatile 的原始语义定义不明确,这意味着使用 volatile 不是解决方法,pre Java 5.)

如果您问是否可以在没有 volatile 的情况下执行 DCL,那么答案是否定的。Java 5 内存模型更改不会 "fix" 最初的 Java 实现具有非易失性 instance 变量的 DCL。

如果您问将 DCL 用于惰性初始化单例是否仍然是个好主意,那么答案是否定的。(在我看来):

  • 有更好的方法来实现延迟初始化的单例。使用 enum 就是其中之一。

  • 既然DCL惯用语仍然容易出错且理解不佳1,最好避免使用。

  • 同步性能的改进在很大程度上消除了对 DCL 的需求。


Enum and static init will initialize the singletone on class load (correct me if i'm mistaken).

我认为你错了。 Class 初始化也偷懒了。它不会在 class 加载时发生,除非你强制它;例如通过使用 3-arg overload of Class.forName. JLS 12.4.1 设置确定何时发生的规则。

结果是您可以确保基于枚举的单例的初始化是延迟发生的,并且肯定会安全地完成。

顺便说一句,延迟初始化的硬性要求让我想到您的应用程序设计中存在问题。至少,它引入了一个脆弱点......无论惰性初始化如何实现。


1 - 如果 "average Joe programmer" 不理解 DCL 的复杂性,那么在他可能需要维护的代码中使用 DCL 是个坏主意。你比一般的 Joe 程序员更聪明这一事实没有实际意义。