DCL单例模式设计为什么需要volatile修饰实例对象
                           
天天向上
发布: 2025-07-28 20:46:12

原创
754 人浏览过

DCL(Double-Checked Locking,双重检查锁)单例模式中,volatile 关键字对于保证 线程安全正确的实例初始化绝对必要 的。我们来系统地解析为什么 DCL 中必须使用 volatile 修饰单例实例对象。


一、DCL 单例模式的结构(不含 volatile

public class Singleton {
    private static Singleton instance;

    public static Singleton getInstance() {
        if (instance == null) {  // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) {  // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

这个实现目的是为了既避免重复创建实例,又能在第一次创建后避免每次访问都进入 synchronized,从而提高性能。


二、问题:为什么不加 volatile 会出错?

1. new Singleton() 不是原子操作

instance = new Singleton();

这行语句在 JVM 中会被拆分为 3 步操作(指令重排序前):

1)分配内存给对象
2)调用构造函数初始化对象
3)将对象引用赋值给变量(instance 指向这块内存)

但由于 JVM 允许指令重排序,实际执行可能变成这样:

1)分配内存
2)将对象引用赋值给变量
3)调用构造函数初始化对象

2. 重排序导致的问题:

如果线程 A 执行了重排序并在 step 2 赋值完后被线程 B 读取:

  • 线程 B 发现 instance != null,于是跳过同步块,直接返回。
  • 但此时 instance 其实是一个 尚未初始化完成 的对象。
  • 线程 B 访问该对象时可能引发 空指针异常 或其他未定义行为。

三、volatile 的作用:防止重排序 + 保证可见性

1. 禁止指令重排序

加上 volatile 后,instance = new Singleton(); 不允许发生 构造完成之前就赋值引用 的操作。

private static volatile Singleton instance;

JVM 层面会通过内存屏障指令(Memory Barrier)阻止指令重排序,确保初始化完成之后,才把对象引用赋值给 instance

2. 保证线程间可见性

  • volatile 能确保一个线程对 instance 的写入,对其他线程可见;
  • 没有 volatile,其他线程可能读到“旧值”或者未初始化值。

四、加上 volatile 的正确 DCL 实现

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {  // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) {  // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

五、总结

原因说明
指令重排序new Singleton() 会被重排序,可能返回未初始化对象
多线程可见性没有 volatile,其他线程可能看不到最新值
volatile 的作用禁止重排序,确保初始化安全,保证内存可见性

六、权威资料参考


更多详细内容请关注其他相关文章!

发表回复 0

Your email address will not be published. Required fields are marked *