我读了一篇关于双重检查锁是如何中断的http://www.ibm.com/developerworks/java/library/j-dcl.html的文章,以及关于堆栈溢出的一些相关问题。
我已经用过这个模式/习语好几次了,没有任何问题。由于我一直在使用Java 5,我的第一个想法是在Java 5内存模型中纠正了这种情况。然而,这篇文章说:
本文引用了Java内存模型( Java ),然后将其修改为Java5.0;关于内存排序的语句可能不再正确。然而,的双重检查锁定成语仍然在新的内存模型下被打破.
这是一个真正的问题吗?如果是的话,在什么情况下?
发布于 2010-08-18 20:09:07
举例说明单例中关于双检查锁的示例,该锁看起来很聪明,但已损坏。
同步块的开始保证您看到了最新的数据,但它并不保证重新排序,除非您也处于同步块中,否则不能期望数据的一致视图。它并不保证在同步部分中完成的变量修改对其他线程是可见的。只有进入同步块的线程才能看到更改。这就是为什么双重检查锁定被破坏的原因--它在读取器方面不是同步的。读取线程可能会看到,单例数据不是空的,但是单例数据可能没有完全初始化(可见)。
另一方面,易失性提供排序,以保证排序,例如,写入易失性单例静态字段,保证写入到单例对象的静态字段将在写入易失性静态字段之前完成。它不阻止单独创建两个对象;这是通过同步提供的。类最终静态字段不需要是易失性的。在Java中,JVM解决了这个问题。
更多内容可在以下文件中找到:
发布于 2010-03-20 06:13:06
对于某个人来说,很难确定他们的应用程序实际上受到了双重检查的锁故障的影响。事实上,许多使用这个成语的应用程序可能由于各种原因而从未经历过这个问题。
然而,这并不意味着你应该使用它。仅仅有一个不可量化的失败概率就足以说服你不要使用双重检查的锁定,特别是因为有安全的选择。
你只是幸运而已。
发布于 2010-03-20 06:22:02
我们有一个应用程序,它使用了一个破碎的复核成语,而且功能很好地运行了很长一段时间--不,事实上,我从来没有遇到过这个习语的问题。当然,不管怎么说,我都修好了。
我想原因之一是线程的可见性最终会在现实世界中实现。一旦达到,它就会继续存在。所以,是的,很难发现这个问题是否已经发生。
我认为hashCode()实现String部分依赖于这个事实.线程在看不到缓存时计算hashCode,但最终它们开始看到。同时,重复计算意味着只浪费了一些CPU时间,避免易失性语义的内存效应的好处超过了这种浪费的努力(至少这就是他们以这种方式实现它的原因)。有效使用的成语是(实际的String.hashCode()实现):
/** Cache the hash code for the string */
private int hash; // Defaults to 0
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}显然,在使用它之前,一个人必须思考和衡量很多。
https://stackoverflow.com/questions/2482092
复制相似问题