懒汉式单例模式到底需不需要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;
    }


}
经验分享 程序员 微信小程序 职场和发展