volatile关键字仅保证可见性和禁止指令重排序,不保证原子性;典型正确用法是修饰单次读写的共享状态标志(如running),常见误区包括用于复合操作、非共享变量或误以为能解决所有线程安全问题。

volatile 关键字通过强制线程每次读取都从主内存加载、每次写入都立即刷回主内存,并使其他 CPU 缓存中对应变量的副本失效,来保证多线程间变量的可见性。
它解决的是“改了但看不到”的问题
Java 线程默认把变量缓存在自己的工作内存(如 CPU 寄存器或 L1/L2 缓存)里,不总去主内存同步。这就导致:线程 A 修改了 flag = true,但还没刷回主内存时,线程 B 仍在用自己缓存里的 false 死循环。
volatile 强制两点:
- 每次读 volatile 变量,都跳过本地缓存,直接从主内存加载最新值
- 每次写 volatile 变量,都立即写回主内存,并触发缓存一致性协议(如 MESI),让其他 CPU 缓存中该变量所在缓存行失效
典型安全用法:布尔状态标志
这是最常用也最容易出错的场景,核心是只做“单次赋值 + 单次读取”,不掺杂其他逻辑。
立即学习“Java免费学习笔记(深入)”;
正确示例:
private volatile boolean running = true;public void run() {
while (running) { // 每次都从主内存读
doWork();
}
}
public void shutdown() {
running = false; // 立即写回主内存
}
常见误区要避开
- 在 while(running) 循环体里又调用 shutdown() —— 这不是 volatile 能保的,是业务逻辑耦合问题
- 把 volatile 变量和其他非 volatile 字段混用判断,比如 if (running && count > 0),后者不可见
- 用 volatile Boolean(包装类)代替 volatile boolean —— 自动拆箱可能空指针,且 null 状态本身无同步语义
- 认为 volatile List<String> list 能让 add("x") 的内容也可见 —— 它只保证 list 引用地址变更可见,不保证内部元素可见
它不负责的事:原子性和重排序之外的协同
volatile 保证单次读/写操作的可见性与禁止重排序,但不保证复合操作原子性。例如:
- count++ 是读-改-写三步,volatile int count 无法防止丢失更新
- 多个 volatile 字段之间没有执行顺序约束,不能靠它们实现跨字段的状态协同
- 单例双重检查中使用 volatile Singleton instance,主要是为禁止构造过程重排序,属于“初始化安全性”,和单纯可见性不是一回事


















