懒汉式单例模式到底需不需要volatile
最近一直有个疑问,创建线程安全的懒汉式单例模式到底需不需要volatile,为此深究了一下。
首先先写一个不加的情况:
/** * @author PanBangLin * @version 1.0 * @date 2022/7/3 11:26 */ public class SingleTest { private static SingleTest SINGLE; private SingleTest() { }; public SingleTest getSINGLE(){ // double check lock 双重检查锁 if(SINGLE == null){ // 第一次判断 synchronized (SingleTest.class){ if (SINGLE == null){ // 第二次判断 SINGLE = new SingleTest(); } } } return SINGLE; } }
为什么要使用双重检测锁呢?因为在并发的情况下,有可能有多个线程同时符合了第一次判断的情况,进入到了synchronized处等待,等到第一个线程实例化好对象后,将锁释放,后面卡在锁上的线程又会陆续实例化对象,造成重复实例化。 继续回到 volatile 的问题,了解这个问题,需要先明白对象实例化的过程:
op1:分配内存空间 op2:初始化对象 op3:将对象的引用,指向分配的内存
如果发生指令重排
op1:分配内存空间 op2:将对象的引用,指向分配的内存 op3:初始化对象
那么如果在第一个线程实例化的过程中,后续再有线程进入到了第一次判断不等于null的部分,那么会直接返回这个半实例化的对象,因为这个时候SINGLE 已经不等于null了,但是这个对象是无法使用的,后面这个线程调用的时候就会发生报错。 所以我们需要把这个变量加上 volatile关键字,让其不发生指令重排,以及修改后的值马上同步到主存上去。 最终效果:
/** * @author PanBangLin * @version 1.0 * @date 2022/7/3 11:26 */ public class SingleTest { private volatile static SingleTest SINGLE; private SingleTest() { }; public SingleTest getSINGLE(){ // double check lock 双重检查锁 if(SINGLE == null){ // 第一次判断 synchronized (SingleTest.class){ if (SINGLE == null){ // 第二次判断 SINGLE = new SingleTest(); } } } return SINGLE; } }
下一篇:
SPL学习笔记(1)---概念