真实Java应用程序中的单例模式和中断的双重检查锁定

真实Java应用程序中的单例模式和中断的双重检查锁定,java,singleton,double-checked-locking,Java,Singleton,Double Checked Locking,我在读这篇文章,关于双重检查锁定是如何被破坏的,以及这里有关StackOverflow的一些相关问题 我已经多次使用这种模式/习惯用法,没有任何问题。由于我一直在使用Java5,我的第一个想法是,这已经在Java5内存模型中得到了纠正。然而,文章说: 本文涉及Java内存 在它被修改为Java之前的模型 5.0; 关于内存顺序的语句可能不再正确但是 双重检查锁定习惯用法仍然适用 在新的内存模式下出现故障 这是一个真正的问题吗?如果是的话,在什么情况下?对于某些人来说,很难确定他们的应用程序确实受

我在读这篇文章,关于双重检查锁定是如何被破坏的,以及这里有关StackOverflow的一些相关问题

我已经多次使用这种模式/习惯用法,没有任何问题。由于我一直在使用Java5,我的第一个想法是,这已经在Java5内存模型中得到了纠正。然而,文章说:

本文涉及Java内存 在它被修改为Java之前的模型 5.0; 关于内存顺序的语句可能不再正确但是 双重检查锁定习惯用法仍然适用 在新的内存模式下出现故障


这是一个真正的问题吗?如果是的话,在什么情况下?

对于某些人来说,很难确定他们的应用程序确实受到了双重检查锁定故障的影响。事实上,由于各种原因,许多使用此习惯用法的应用程序可能永远不会遇到此问题

然而,这并不意味着你应该使用它。仅仅存在不可量化的失败概率这一事实就足以说服您不要使用双重检查锁定,特别是因为存在安全的替代方案


你真幸运。

我们有一个应用程序,它使用了一个不正确的双重检查习惯用法,并且它在很长一段时间内运行得非常完美-不,事实上,我从来没有遇到过这个习惯用法的问题。当然,不管怎么说,我都把它修好了

我想其中一个原因是线程可见性最终将在现实世界中实现。一旦获得,它就会停留。因此,是的,很难发现问题是否发生了

我相信
String
hashCode()
实现部分依赖于这个事实。。。线程在看不到缓存时计算哈希代码,但最终开始看到缓存。同时,重复计算意味着浪费一些时间,避免易失性语义的记忆效应的好处胜过了这种浪费的努力(至少我想这就是他们以这种方式实现它的原因)。有效使用的习惯用法是(实际的String.hashCode()实现):

/**缓存字符串的哈希代码*/
私有整数散列;//默认为0
公共int hashCode(){
int h=散列;
如果(h==0){
int off=偏移量;
char val[]=值;
int len=计数;
对于(int i=0;i
显然,在使用它之前,必须考虑和测量很多。

举例说明了一个关于双重检查锁定的单例,它看起来很聪明,但被破坏了 同步块的开始可以保证看到最新的数据,但不能保证重新排序,除非 也在同步块中。它不能保证在synchronized部分中完成的变量修改对其他线程可见。只有进入同步块的线程才能保证看到更改。这就是双重检查锁定被破坏的原因——它在读卡器端不同步读取线程可能会看到,单例数据不是空的,但是单例数据可能没有完全初始化(可见)

另一方面,顺序是由volatile提供的,volatile保证了顺序,例如write to volatilesingleton static field保证了对singleton对象的写入将在对volatile static field的写入之前完成。它不阻止创建两个对象的单例;这是由同步提供的。 类的最终静态字段不需要是可变的。在Java中,JVM处理这个问题

更多信息请参见:


如果您使用的是java 5,checkout…String hashCode就是一个例子,在这种情况下,两个线程可能执行相同的工作并不重要。这表明并不总是需要锁定。这不是一张损坏的双重支票。中断的双重检查具有模式
if(test){synchronized{if(test){//…}}}
/** 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;
 }