设计模式之单例DCL为什么需要volatile

单例模式DCL实现

pulic class Singleton {
    //私有构造函数,方式外部通过new创建对象
    private Singleton () {
    }
    //类的内部声明变量
    //volatile防止指令重排
    private static volatile Singleton singleton;

    //对外暴露一个静态方法,当调用该方法时,才去创建实例(singleton)
    //加入双重检查,解决线程安全问题,同时支持Lazy Loading,同时保证了效率
    //推荐使用
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

不加volatile会有什么问题?
程序执行过程中, 为了性能考虑, 编译器和CPU可能会对指令重新排序。
在极少数的情况下,下面语句会发生指令重排

singleton = new Singleton();

以上语句经过编译后,可分为以下三步(伪代码)

memory = allocate(); // 1:分配对象的内存空间
instance(memory); // 2:初始化对象
instance = memory; // 3:设置instance指向刚分配的内存地址

以上步骤2与步骤3不存在数据依赖关系,而且无论重排前或者重排后程序的执行结果在单线程中没有变化,因此这种重排优化是允许的。所以存在以下顺序执行情况:

memory = allocate(); // 1:分配对象的内存空间
instance = memory; // 3:设置instance指向刚分配的内存地址
instance(memory); // 2:初始化对象

如果执行顺序为1.3.2,当A线程执行完1,3(此时还未完成对象初始化),这时B线程第一次判断singleton == null,得到的结果未false,然后就会直接return一个空的对象,继而产生后续错误。

为什么需要volatile?
volatile可实现禁止指令重排,防止上述情况发生。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章