volatile关键字在多线程中最典型的作用是解决内存可见性问题:一个线程修改变量后,其他线程能立即看到最新值;其原理是通过内存屏障强制读写主内存并使缓存行失效,适用于一写多读的原子状态标志,但不保证复合操作原子性。

volatile 关键字在多线程中解决的最典型问题,就是内存可见性——一个线程改了值,另一个线程能立刻“看到”。它不加锁、不开销大,但也不是万能的;用对场景,效果立竿见影。
为什么普通变量会“看不见更新”?
Java 内存模型(JMM)规定:每个线程有自己的工作内存(类似 CPU 缓存),变量读写都先在工作内存中进行。主内存是共享的,但线程之间不直接通信。
比如一个 boolean running = true,线程 A 修改为 false,只更新了自己工作内存里的副本;线程 B 仍从自己的缓存里读到 true,循环永远不退出——这就是典型的不可见。
根本原因包括:
• CPU 多级缓存导致副本不一致
• JVM 或编译器可能把 while(running) 优化成常量判断(如缓存 flag 值、提前加载)
• 写操作未强制刷回主内存,读操作未强制重载最新值
volatile 是怎么让变量“立刻可见”的?
它通过两层机制协同生效:
- 写操作时:JVM 插入 StoreStore + StoreLoad 内存屏障,确保该变量新值立即写入主内存,并使其他 CPU 缓存中的对应缓存行失效(MESI 协议触发总线嗅探)
- 读操作时:强制从主内存重新加载该变量值,跳过工作内存旧副本
这相当于给变量装了个“实时同步开关”,不需要锁,但代价是每次读写都绕过缓存直连主内存,性能略低(对高频读写变量需权衡)。
哪些场景适合用 volatile?
它适用于“一写多读”且操作本身具备原子性的简单状态标志:
- 线程启停控制:如 volatile boolean shutdownRequested
- 初始化完成标记:如 volatile boolean initialized,配合双重检查锁(DCL)中的 instance
- 状态切换信号:如 volatile int status(仅赋值,不 ++/--)
⚠️ 注意:volatile 不保证复合操作的原子性。例如 volatile int count 的 count++ 仍是三步(读-改-写),多个线程并发执行会导致丢失更新——这种必须用 AtomicInteger 或 synchronized。
对比 synchronized 和 volatile 的关键区别
两者都能实现可见性,但设计目标不同:
- synchronized:提供互斥 + 可见性 + 原子性。进入/退出时强制刷新工作内存与主内存,但会阻塞其他线程
- volatile:仅提供可见性 + 禁止重排序,无锁、无阻塞,轻量高效,但不能防竞态
简单说:要“保护一段代码逻辑不被并发干扰”,选 synchronized;只要“让一个布尔开关或状态值及时广播出去”,volatile 更合适。











