几篇合集。
1 单例模式“双重检查锁定Double-Checked Locking”线程安全问题
https://blog.csdn.net/wabiaozia/article/details/84723899
2 主题:用happen-before规则重新审视DCL
https://blog.csdn.net/wabiaozia/article/details/84727407
3 Double-checked locking: Clever, but broken
http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#dcl
4 Does the new memory model fix the "double-checked locking" problem?
https://blog.csdn.net/wabiaozia/article/details/84839566
注:
1 1和 3.4三篇写的比较早,有些问题已经处理更新了。可以看文章下的一些评论。
2 volatile屏蔽指令重排序的语义在JDK1.5中才被完全修复,此前的JDK中及时将变量声明为volatile,也仍然不能完全避免重排序所导致的问题(主要是volatile变量前后的代码仍然存在重排序问题),这点也是在JDK1.5之前的Java中无法安全使用DCL来实现单例模式的原因
3 在java5之前对final字段的同步语义和其它变量没有什么区别,在java5中,final变量一旦在构造函数中设置完成(前提是在构造函数中没有泄露this引用),其它线程必定会看到在构造函数中设置的值。而DCL的问题正好在于看到对象的成员变量的默认值,因此我们可以将LazySingleton的someField变量设置成final,这样在java5中就能够正确运行了。
以下是原文:
Does the new memory model fix the "double-checked locking" problem?
The (infamous) double-checked locking idiom (also called the multithreaded singleton pattern) is a trick designed to support lazy initialization while avoiding the overhead of synchronization. In very early JVMs, synchronization was slow, and developers were eager to remove it -- perhaps too eager. The double-checked locking idiom looks like this:
// double-checked-locking - don't do this!
private static Something instance = null;
public Something getInstance() {
if (instance == null) {
synchronized (this) {
if (instance == null)
instance = new Something();
}
}
return instance;
}
This looks awfully clever -- the synchronization is avoided on the common code path. There's only one problem with it -- it doesn't work. Why not? The most obvious reason is that the writes which initialize instanceand the write to the instance field can be reordered by the compiler or the cache, which would have the effect of returning what appears to be a partially constructed Something. The result would be that we read an uninitialized object. There are lots of other reasons why this is wrong, and why algorithmic corrections to it are wrong. There is no way to fix it using the old Java memory model. More in-depth information can be found at Double-checked locking: Clever, but broken and The "Double Checked Locking is broken" declaration
Many people assumed that the use of the volatile keyword would eliminate the problems that arise when trying to use the double-checked-locking pattern. In JVMs prior to 1.5, volatile would not ensure that it worked (your mileage may vary). Under the new memory model, making the instance field volatile will "fix" the problems with double-checked locking, because then there will be a happens-before relationship between the initialization of the Something by the constructing thread and the return of its value by the thread that reads it.
However, for fans of double-checked locking (and we really hope there are none left), the news is still not good. The whole point of double-checked locking was to avoid the performance overhead of synchronization. Not only has brief synchronization gotten a LOT less expensive since the Java 1.0 days, but under the new memory model, the performance cost of using volatile goes up, almost to the level of the cost of synchronization. So there's still no good reason to use double-checked-locking. Redacted -- volatiles are cheap on most platforms.
Instead, use the Initialization On Demand Holder idiom, which is thread-safe and a lot easier to understand:
private static class LazySomethingHolder {
public static Something something = new Something();
}
public static Something getInstance() {
return LazySomethingHolder.something;
}
This code is guaranteed to be correct because of the initialization guarantees for static fields; if a field is set in a static initializer, it is guaranteed to be made visible, correctly, to any thread that accesses that class.