volatile 在单例模式中为什么必要
在 java 的双重检查锁定(dcl)单例模式中,volatile 关键字是必要的,因为它禁止了指令重排序。如果没有 volatile,实例化过程可能被拆分为内存分配、初始化对象和赋值引用三个步骤,其中后两步可能重排序。这会导致其他线程获取到一个尚未初始化的半初始化对象引用,从而引发错误。

没有 volatile 时的指令重排序风险
当 instance 变量未声明为 volatile 时,JVM 允许对代码进行优化和重排序。在多线程环境下,一个线程执行到赋值操作时,另一个线程可能已经通过了空值检查并使用了该对象,此时对象的状态是不完整的。

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;
}
}
预期结果:在没有 volatile 的情况下,虽然大多数时候程序能正常运行,但在高并发场景下存在极小概率出现 NullPointerException 或对象状态不一致的问题。
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
使用 volatile 后的安全机制
将 instance 声明为 volatile 后,JVM 会确保对该变量的读写操作具有可见性,并且禁止了指令重排序。这意味着在赋值操作完成之前,对象一定已经被完全初始化,其他线程读取到的要么是 null,要么是完全初始化的对象。

public class SingletonVolatile {
private static volatile SingletonVolatile instance;
public static SingletonVolatile getInstance() {
if (instance == null) {
synchronized (SingletonVolatile.class) {
if (instance == null) {
instance = new SingletonVolatile();
}
}
}
return instance;
}
}
预期结果:使用 volatile 后,双重检查锁定模式变得线程安全,能够正确返回完全初始化的单例对象。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










