我正在阅读这篇关于"双重检查锁定"的文章,并且在文章的主题之外我想知道为什么在文章的某些方面作者使用下一个成语:
清单7.尝试解决乱序写入问题
public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { //1 Singleton inst = instance; //2 if (inst == null) { synchronized(Singleton.class) { //3 inst = new Singleton(); //4 } instance = inst; //5 } } } return instance; }
我的问题是:有没有理由用同一个锁同步两次代码?有这个任何目的吗?
提前谢谢了.
锁定两次的目的是试图防止无序写入.内存模型指定了重新排序的位置,部分是根据锁定.锁定确保在"instance = inst;"之后没有写入(包括单例构造函数中的任何内容).线.
然而,为了更深入地探讨这个话题,我推荐Bill Pugh的文章.然后永远不要尝试:)
本文引用了5.0之前的Java内存模型(JMM).在该模型下,将同步块强制写入主存储器.因此,它似乎是尝试确保Singleton对象在引用之前被推出.然而,它并不是很有效,因为实例写入可以移动到块中 - 蟑螂汽车旅馆.
但是,5.0之前的模型从未正确实现.1.4应遵循5.0模型.类是懒惰地初始化的,所以你不妨写一下
public static final Singleton instance = new Singleton();
或者更好的是,不要使用单身人士,因为他们是邪恶的.
Jon Skeet是对的:阅读Bill Pugh的文章.汉斯使用的成语是不起作用的精确形式,不应该使用.
这是不安全的:
private static Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }
这也不安全:
public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { //1 Singleton inst = instance; //2 if (inst == null) { synchronized(Singleton.class) { //3 inst = new Singleton(); //4 } instance = inst; //5 } } } return instance; }
永远都不要做他们中的任何一个.
相反,同步整个方法:
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
除非您每秒检索此对象数十亿次,否则实际上的性能损失可以忽略不计.