据我所知,对于旧的JMM,实现懒单线的DCL(双重检查锁定)技巧被打破了,但我认为它是用新的JMM和易失性字段修复的......
然而在this好的文章中,显然是新的,需要在DCL中引用新的和旧的JMM和volatile字段,它仍然被打破......
在这里和那里我读到它已被修复然后我发现了这个...有人可以说最后它是否已损坏?
我的理解是,挥发性保证关系之前的hapens并且有效地发布了membar解决问题并且DCL现在有效...尽管我同意静态懒惰初始化更容易理解...
答案 0 :(得分:10)
在这里和那里我读到它已被修复然后我发现了这个...有人可以说最后它是否已损坏?
这取决于你的意思"它"。
如果您问是否可以使用volatile执行DCL,则答案为是,发布Java 5.(volatile
的原始语义未明确定义,这意味着使用volatile
在Java 5之前没有修复。)
如果您在询问是否可以在没有易失性的情况下进行DCL,那么答案就是“否”.Java 5内存模型的更改不会修复"具有非易失性instance
变量的DCL的原始Java实现。
如果你问是否仍然使用DCL进行懒惰的初始化单例,那么答案是否定的(在我看来):
有更好的方法来实现一个懒惰的初始化单例。使用enum
就是其中之一。
由于DCL习语仍然容易出错并且不太清楚 1 ,因此最好避免使用它。
同步性能的提升在很大程度上消除了对DCL的需求。
枚举和静态初始化将在类加载时初始化单线态(如果我错了,请更正我)。
我认为你错了。类初始化也很懒惰。除非你强迫它,否则它不会在课堂加载时发生;例如使用Class.forName
的{{3}}。 3-arg overload列出了确定何时发生的规则。
结果是你可以确保基于枚举的单例的初始化是懒散的,并且它肯定会安全地完成。
另外,懒惰初始化的硬性要求提示我应用程序设计中存在问题。至少,它引入了一个脆弱点...无论如何实现延迟初始化。
1 - 如果一个普通的Joe程序员"我不了解DCL的复杂性,因此在他可能需要维护的代码中使用DCL是一个坏主意。事实上,你比普通的Joe程序员更聪明。
答案 1 :(得分:4)
它已在Java 5中修复。
然而这些天来,"正确" (即最简单的方法)是使用枚举进行延迟初始化。
public enum Singleton {
INSTANCE;
// No need for a getInstance() method
//public static Singleton getInstance() {
// return INSTANCE;
//}
// Add methods to your liking
}