未用volatile与全方法同步的双重检查锁单例是否线程安全?
为什么你的双重检查锁单例不是线程安全的
你好呀!你的这个双重检查锁实现确实存在线程安全问题,核心原因和Java对象初始化时的指令重排序有关,咱们来一步步拆解:
问题根源:对象初始化的指令重排序
当执行new MySingleton()时,JVM实际会做三件事:
- 为
MySingleton对象分配内存空间
- 为
- 调用构造函数,初始化对象的成员变量
- 将
mySingleton引用指向刚分配的内存地址
- 将
为了优化执行效率,JVM允许在不影响单线程执行结果的前提下对这三个步骤进行重排序。也就是说,实际执行顺序可能变成:1 → 3 → 2。这时候就会出现线程安全隐患。
线程不安全的具体场景
假设现在有两个线程A和B同时调用getInstance():
- 线程A进入第一个
if(mySingleton == null)判断,发现引用为空,进入同步代码块 - 线程A执行
new MySingleton(),JVM做了指令重排序:先把mySingleton引用指向内存地址(步骤3),但还没完成对象初始化(步骤2) - 这时候线程B进入第一个
if(mySingleton == null)判断,发现mySingleton已经不为空(因为指向了内存地址),直接返回这个未完全初始化的对象 - 线程B尝试使用这个对象时,就会遇到各种问题:比如成员变量还是默认值、抛出
NullPointerException,或者其他不符合预期的行为
如何修复?加上volatile关键字
volatile关键字有两个关键作用:
- 禁止JVM对该变量相关的指令进行重排序
- 保证变量的可见性(一个线程对变量的修改,其他线程能立刻看到)
修正后的代码如下:
public class MySingleton { // 加上volatile关键字 private static volatile MySingleton mySingleton; public static MySingleton getInstance() { if(mySingleton == null) { synchronized(MySingleton.class) { if(mySingleton == null) { MySingleton temp = new MySingleton(); mySingleton = temp; } } } return mySingleton; } }
加上volatile后,JVM就不会对new MySingleton()的初始化步骤进行重排序,确保线程看到的mySingleton要么是null,要么是完全初始化完成的对象,这样双重检查锁就能保证线程安全了。
内容的提问来源于stack exchange,提问作者Batman
相关产品推荐
相关产品推荐

