使用双锁时使单例实例易变的重点是什么?

Pho*_*nix 33 java singleton volatile double-checked-locking thread-synchronization

private volatile static Singleton uniqueInstance
Run Code Online (Sandbox Code Playgroud)

在单独使用双锁方法进行同步时,为什么单个实例声明为volatile?我可以在不将其声明为volatile的情况下实现相同的功能吗?

das*_*ght 54

volatile防止记忆被重新排序写道,使它不可能为其他线程通过单的指针读你单身的未初始化的领域.

考虑这种情况:线程A发现uniqueInstance == null锁定,确认它仍然存在null,并调用singleton的构造函数.构造函数XYZ在Singleton中写入成员,然后返回.线程A现在将对新创建的单例的引用写入uniqueInstance,并准备释放其锁.

正如线程A准备释放其锁定一样,线程B出现,并发现uniqueInstance不是null.线程B访问uniqueInstance.XYZ认为它已被初始化,但由于CPU已重新排序写入,线程A写入的数据XYZ尚未对线程B可见.因此,线程B内部看到不正确的值XYZ,这是错误的.

标记uniqueInstancevolatile时,会插入内存屏障.uniqueInstanceuniqueInstance修改之前启动的所有写入将在修改之前完成,从而防止上述重新排序情况.

  • 谢谢你的回答 (2认同)
  • @NeelabhSingh,从字面上看,它的意思是 - 如果你的代码说它想在位置 B 之前写入位置 A,则使用“易失性”可确保 A 实际上在 B 之前写入。如果没有“易失性”,CPU 可以自由地将 B 写在 A 之前,只要它对代码逻辑没有明显的影响即可。 (2认同)

Mar*_*ers 26

没有volatile代码,多线程无法正常工作.

来自维基百科的双重锁定:

从J2SE 5.0开始,此问题已得到修复.volatile关键字现在确保多个线程正确处理单例实例.这个新的成语在"双重锁定已破损"声明中有所描述:

// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
    private volatile Helper helper = null;
    public Helper getHelper() {
        Helper result = helper;
        if (result == null) {
            synchronized(this) {
                result = helper;
                if (result == null) {
                    helper = result = new Helper();
                }
            }
        }
        return result;
    }

    // other functions and members...
}
Run Code Online (Sandbox Code Playgroud)

一般情况下,如果可能的话,应该避免重复检查锁定,因为很难做到正确,如果你弄错了,很难找到错误.请尝试这种更简单的方法:

如果辅助对象是静态的(每个类加载器一个),则另一种选择是按需定位器习惯用法初始化

// Correct lazy initialization in Java 
@ThreadSafe
class Foo {
    private static class HelperHolder {
       public static Helper helper = new Helper();
    }

    public static Helper getHelper() {
        return HelperHolder.helper;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在某些情况下,如果您有一个可在运行时更改的单例,但在程序中可能只存在一次,则可能需要进行双重检查锁定.即您要用于访问网站的登录会话,但您必须重新连接并偶尔获取一个新会话.但是,如果在运行时期间值没有改变,则应该避免使用它 (2认同)

Pet*_*rey 11

为避免使用双重锁定或易失性,我使用以下内容

enum Singleton {
     INSTANCE;
}
Run Code Online (Sandbox Code Playgroud)

创建实例很简单,延迟加载和线程安全.