volatile 关键字通过强制读写主内存和禁止重排序来保证可见性,适用于状态标志等简单场景,但不保证原子性、互斥性及复合操作的线程安全。

volatile 关键字在 Java 中不保证原子性,但能强制线程每次读取变量时都从主内存读,每次写入都立即刷回主内存,从而解决多线程下的可见性问题。
volatile 如何确保可见性
Java 内存模型(JMM)规定:每个线程有自己的工作内存(如 CPU 缓存),变量副本可能被缓存在其中。普通变量的读写不强制同步到主内存,导致一个线程修改后,其他线程看不到最新值。
而 volatile 变量的读操作会“嗅探”主内存,写操作会“刷新”主内存,并且编译器和处理器不会对 volatile 读写做重排序(有 happens-before 语义),这就让修改对其他线程立即可见。
典型适用场景
适合用于状态标志、一次性初始化、轻量级信号通知等简单读写场景:
- 用作“开关”标志:比如 private volatile boolean running = true;,另一个线程设置 running = false; 后,执行循环的线程能及时看到变化并退出
- 双重检查锁定(DCL)中修饰单例对象引用:private static volatile Singleton instance;,防止指令重排序导致其他线程拿到未构造完成的对象
- 不适用于 count++ 这类复合操作(读-改-写),因为 volatile 不能保证该操作的原子性
volatile 的局限性
它只解决可见性和部分有序性,不解决:
- 原子性:i++ 不是原子操作,即使 i 是 volatile,仍可能丢失更新
- 互斥性:无法替代 synchronized 或 Lock 实现临界区保护
- 复合逻辑:比如 “先检查再设置”(if (flag == false) flag = true)不是原子的,仍需同步机制
和 synchronized 对比
两者都能保证可见性,但方式不同:
- synchronized 在进入/退出时,会把工作内存中涉及的变量全部清空,并强制从主内存重新加载(解锁前也刷新)
- volatile 更轻量,无锁、无上下文切换开销,但功能更单一
- 如果需要原子性或多个变量协同变化,优先考虑 synchronized 或 java.util.concurrent 工具类
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











