volatile仅保证标志位读写可见性与禁止重排序,不保证复合操作原子性;适用于单写多读的状态通知,禁用于读-改-写等非原子场景。

加 volatile 是最直接、开销最小的解法,但只适用于“单写多读”的标志位场景;一旦涉及复合操作(比如先读再写),它就失效了。
为什么普通布尔变量在多线程中看不到修改?
线程 A 修改了 stop 为 true,线程 B 却一直卡在 while (!stop) 里 —— 这不是代码逻辑错,而是 JVM 允许线程把变量缓存在自己的工作内存(CPU 缓存)中,不主动同步到主内存,也不主动从主内存刷新。结果就是:线程 B 永远读不到线程 A 写进去的新值。
常见错误现象包括:
- 循环不退出,看似“卡死”
- 状态变更后,其他线程感知延迟明显(几毫秒甚至更久)
- 在某些 CPU 架构或 JVM 版本下表现不稳定(比如本地测试正常,压测环境复现失败)
怎么正确声明 volatile 标志位?
只需在变量声明时加上 volatile 关键字,且必须是实例变量或静态变量(局部变量不能用 volatile)。
示例:
private static volatile boolean stop = false; // ✅ 正确:static + volatile,多线程可见
注意以下几点:
-
volatile不能修饰局部变量(编译报错:illegal start of expression) - 不要和
final一起用(final volatile合法但无意义,因为final变量不可变) - 如果变量是对象引用(如
volatile List),只保证引用本身的可见性,不保证其内部元素的线程安全
volatile 能不能用于 i++ 这类操作?
不能。标志位场景里,你通常只做赋值(flag = true)或读取(if (flag)),这是安全的;但像 counter++ 这种操作包含“读-改-写”三步,volatile 不提供原子性保障。
典型错误写法:
private static volatile int counter = 0; // ❌ 下面这行不是原子操作,多线程下仍可能丢失更新 counter++;
如果你发现标志位需要配合计数、切换状态、或条件判断后再赋值,说明它已超出 volatile 的能力边界,该换 synchronized 或 AtomicBoolean 了。
容易被忽略的细节:JVM 优化与硬件屏障
volatile 的效果依赖于 JVM 插入的内存屏障(memory barrier),它强制:
- 写操作后立即刷回主内存(store barrier)
- 读操作前清空本地缓存,强制从主内存加载(load barrier)
但这不是“实时广播”,只是确保“下一次读一定看到上一次写”。所以别指望它能替代消息通知或回调机制。另外,在低版本 JDK(如 Java 5 以前)中,volatile 的语义不完整,现在基本不用考虑 —— 但如果你维护的是老系统,得确认 JDK 版本是否 ≥ 1.5。










