volatile 解决多线程下变量更新不可见和指令重排序问题;通过内存屏障保证读写可见性与顺序性,但不保证复合操作原子性,适用于状态标志、DCL单例等有限场景。

volatile 解决的是多线程下「变量改了别人看不见」和「代码执行顺序不对」这两个隐形但致命的问题。
为什么普通变量在多线程里会“看不见更新”?
因为每个线程都有自己的工作内存(对应 CPU 缓存),读写 flag 时默认操作的是本地副本,而不是主内存。主线程把 flag = true 写进了自己缓存,但没及时刷回主内存;另一个线程一直从自己缓存读 flag == false,死循环就发生了。
加 volatile 后,JVM 会在写操作后插入 StoreLoad 内存屏障,强制把值同步到主内存;读操作前插入 LoadLoad + LoadStore 屏障,强制从主内存加载最新值。硬件层还配合 MESI 协议让其他 CPU 核心的缓存行失效——双重兜底。
- 现象:线程 A 修改
isRunning,线程 B 死循环不退出 - 修复:声明为
private static volatile boolean isRunning = true; - 注意:仅对单次读/写有效,
count++这种复合操作仍需synchronized或AtomicInteger
为什么双重检查单例必须用 volatile?
不加 volatile 时,instance = new Singleton() 可能被重排序为:分配内存 → 写入引用 → 初始化对象。这时另一个线程看到 instance != null,但拿到的是未初始化完成的对象,一调用方法就 NullPointerException。
立即学习“Java免费学习笔记(深入)”;
volatile 禁止这种重排序,确保「对象构造完成」happens-before「引用赋值」,这是 DCL 能安全工作的唯一前提。
public class Singleton {
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // volatile 阻止此处重排
}
}
}
return instance;
}
}
哪些场景不能靠 volatile 解决?
它不是锁的替代品,只管「一个变量的读写可见性+顺序性」,不管「多个操作的原子性」或「跨变量约束」。
- ❌
if (counter == 0) counter++:判断和自增是两步,volatile 无法保证原子性 - ❌
balance -= amount; accountValid = (balance >= 0);:两个变量之间存在业务约束,volatile 不保证它们的修改顺序可见 - ❌ 需要等待、通知、条件队列等协作机制时,必须用
synchronized或Lock
真正该用 volatile 的地方其实很窄:状态标志(shutdownRequested)、一次性事件通知(initialized)、DCL 中的单例引用——别把它当万能膏药。










