volatile的核心作用是禁止指令重排序,确保instance = new Singleton()的三步(分配内存、初始化对象、赋值引用)严格按序执行,防止多线程下返回半初始化对象。

在单例模式中,volatile 的核心作用是**禁止指令重排序**,确保 instance = new Singleton() 这一语句的三步操作严格按序执行,从而避免多线程下返回未初始化完成的对象。
为什么 new Singleton() 实际分三步?
Java 中创建对象看似一行代码,底层实际包含三个独立动作:
- 为对象分配内存空间(memory = allocate())
- 调用构造方法初始化对象(ctorInstance(memory))
- 将引用赋值给静态变量(instance = memory)
没有 volatile 时会发生什么?
JVM 和 CPU 为提升性能可能对这三步进行重排序。常见危险顺序是:1 → 3 → 2,即:先分配内存、再赋值引用、最后初始化。
此时 instance 已不为 null,但对象内部字段仍是默认值(如 int=0、Object=null)。另一个线程在第一次检查 if (instance == null) 时直接返回该引用,就会拿到一个“半初始化”的实例,引发空指针或逻辑错误。
立即学习“Java免费学习笔记(深入)”;
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
volatile 如何强制保证顺序?
volatile 写操作自带写屏障(Store Barrier),它会插入在“赋值引用”(第3步)之前,禁止编译器和处理器把“初始化对象”(第2步)移到该屏障之后。
等效效果是:JVM 必须确保第2步完成,才能执行第3步;其他线程读取 volatile 变量时,也必须看到第2步已完成的全部结果(可见性保障)。
为什么只加 synchronized 不够?
synchronized 能保证临界区互斥,但它不约束 instance = new Singleton() 内部的指令顺序。即使加锁,若 instance 字段没用 volatile 修饰,仍可能因重排序让其他线程在锁外“提前看到”未初始化的引用。
所以双重检查锁(DCL)必须配合 volatile —— 锁解决“多个线程同时创建”,volatile 解决“创建过程本身不安全”。

















