【多线程】懒汉模式-DCL+volatile

double-checked locking、双检锁、DCL机制还是存在线程安全的问题。
原因是new 操作不是原子操作,存在指令重排的问题
某一个线程在执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化.

一、解决方法:volatile,禁止指令重排

最终版本的单例模式

public class Singleton {
    // 1.持有自己类的属性
    private static volatile Singleton instance;

    // 2.私有的构造方法
    private Singleton() {
    }

    // 3.提供对外获取实例的方法
    // DCL + volatile
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    // 因为 new 操作不是原子操作,所以需要对属性加以volatile修饰,避免重排序
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

二、再分析

DCL(双端检锁) 机制不一定线程安全,原因是有指令重排的存在,加入volatile可以禁止指令重排
原因在于某一个线程在执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化.
instance=new SingletonDem(); 可以分为以下步骤(伪代码)
memory=allocate();//1.分配对象内存空间
instance(memory);//2.初始化对象
instance=memory;//3.设置instance的指向刚分配的内存地址,此时instance!=null

步骤2和步骤3不存在数据依赖关系.而且无论重排前还是重排后程序执行的结果在单线程中并没有改变,因此这种重排优化是允许的.

memory=allocate();//1.分配对象内存空间
instance=memory;//3.设置instance的指向刚分配的内存地址,此时instance!=null 但对象还没有初始化完.
instance(memory);//2.初始化对象

但是指令重排只会保证串行语义的执行一致性(单线程) 并不会关心多线程间的语义一致性
所以当一条线程访问instance不为null时,由于instance实例未必完成初始化,也就造成了线程安全问题.

参考:互联网大厂高频重点面试题

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容