volatile是DCL单例线程安全的必要保障:它禁止指令重排序以防止半初始化对象被访问,并确保实例引用的内存可见性;但必须与双重检查及synchronized配合使用,且仅适用于JDK5及以上版本。

在双重检查锁(DCL)单例模式中,volatile 修饰的是静态实例变量(如 private static volatile Singleton instance),它不是可选项,而是线程安全的必要保障。
防止对象未完全初始化就被其他线程看到
JVM 创建对象实际包含三步:分配内存 → 调用构造器初始化 → 将引用赋值给变量。这三步在无干预时可能被重排序为:分配内存 → 赋值引用 → 初始化。一旦发生这种重排,某个线程就可能拿到一个非 null 但内部字段尚未初始化完成的对象引用,调用其方法时就会出现诡异的空指针或字段默认值异常。
volatile 的写操作具有“禁止指令重排序”语义,能确保构造器执行完毕后,引用才对其他线程可见。
保证多线程间的内存可见性
没有 volatile 时,一个线程在同步块内完成了 instance = new Singleton(),其他线程可能仍从自己的工作内存中读到旧值(null 或过期引用),导致重复创建或使用半初始化对象。
volatile 强制每次读取都从主内存获取最新值,写入也立即刷回主内存,消除线程间缓存不一致问题。
必须配合双重检查 + synchronized 才有效
单独加 volatile 不足以实现线程安全单例——它只解决可见性和有序性,不解决原子性。多个线程仍可能同时进入第一次 if (instance == null) 并争抢锁。
- 第一次检查(外层 if)避免绝大多数情况下的锁竞争
- synchronized 块保证创建动作的互斥性
- 第二次检查(锁内 if)防止多个线程抢到锁后重复创建
- volatile 保证这个唯一创建结果对所有线程正确、及时、完整地可见
仅适用于 Java 5 及以上版本
Java 内存模型(JMM)在 JDK 5 中正式明确了 volatile 的增强语义,包括“写先行发生于后续任意线程的读”。早于 JDK 5 的版本无法可靠支持 DCL,即使加了 volatile 也可能失效。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











