volatile能解决多线程“改了别人却看不到”问题,核心是强制每次读从主内存取、每次写立即刷回主内存;普通变量因线程工作内存缓存导致更新不可见,而volatile通过内存屏障保障可见性,适用于状态标志等场景,但不保证原子性。

volatile 关键字能解决多线程中“改了别人却看不到”的问题,核心是强制每次读都从主内存取、每次写都立刻刷回主内存,不依赖缓存副本。它不加锁、不开销大,但只管“看见”,不管“做完”。
为什么普通变量在多线程里会“看不见更新”
每个线程有自己的工作内存(比如 CPU 缓存),读写变量时默认操作的是本地副本。主线程把 running = false 写进了主内存,子线程可能还在用自己的缓存里读旧值——这不是 bug,是 Java 内存模型(JMM)的默认行为。
典型表现:
- while(running) { } 死循环不退出
- 编译器甚至可能把 running 当常量优化(只读一次),导致循环永远卡住
volatile 怎么让更新“立即可见”
JVM 对 volatile 变量的读写插入内存屏障(Memory Barrier):
- 写操作后:强制把新值刷新到主内存(flag = true → 其他线程马上能见)
- 读操作前:清空本地缓存,直接从主内存加载(if(flag) → 每次都是最新)
- 不依赖锁,也不阻塞线程,开销远低于 synchronized
你声明 private static volatile boolean flag,就是在告诉 JVM:“这个变量,每次读写都给我走主内存。”
volatile 能做什么、不能做什么
能做的(适合场景):
- 状态标志:启动/停止/完成信号(如 isRunning、isDone)
- 轻量级通知:中断标志、开关控制
- 单例双重检查中的 instance 字段(防止返回未初始化对象)
不能做的(常见误区):
- counter++ 这类读-改-写操作:即使 counter 是 volatile,三步非原子,仍会丢更新
- if (flag) doSomething();:flag 的读和 doSomething() 执行之间无原子性保障
- 引用类型变量:只保证引用本身可见,不保证其指向对象内部字段的可见性
用法要点与建议
声明方式很简单:
private volatile boolean shutdownRequested = false;
private static volatile Singleton instance;
关键提醒:
- 不要只为布尔开关加 synchronized —— 锁的复杂度和性能代价远超必要
- 当只需“一写多读、仅需感知变化”时,volatile 是更轻量的选择
- 若涉及复合逻辑、计数、状态链或临界区保护,必须搭配 synchronized、Lock 或原子类

















