volatile保证可见性和禁止指令重排序,通过强制读写主内存、插入内存屏障实现,不提供实时通知或线程唤醒机制,也不保证原子性。

volatile 不提供“实时通知”,它保证的是**可见性**和**禁止指令重排序**,不是线程间主动通信机制。所谓“一个线程修改后另一个线程立刻看到”,本质是绕过本地缓存、强制从主内存读写,而非有通知逻辑。
volatile 如何确保变量修改对其他线程可见
Java 内存模型(JMM)规定:对 volatile 变量的每次读操作,都必须从主内存重新加载;每次写操作,都必须立即刷新到主内存。这绕过了 CPU 缓存(如 L1/L2)和线程工作内存的缓存副本。
- 普通变量可能被线程长期缓存在寄存器或高速缓存中,其他线程看不到更新
- volatile 变量每次读写都会触发内存屏障(Memory Barrier),阻止编译器和处理器对该变量的读写进行重排序
- 例如:线程 A 执行
flag = true(flag 是 volatile),线程 B 后续执行if (flag),B 一定读取最新值(前提是 B 确实执行了该读操作)
volatile 不能替代 synchronized 或 Lock 的原因
它不保证原子性。即使变量本身读写是原子的(如 int、boolean),复合操作(如 i++)仍非原子。
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
-
volatile int count = 0;count++实际包含三步:读 count → 加 1 → 写回 count —— 中间可能被其他线程打断 - 无法用于保护临界区,比如多个 volatile 变量之间的逻辑依赖(如先设 flag=true,再设 data=42),volatile 不能保证这两步的执行顺序对其他线程可见(需用锁或 final + volatile 配合)
典型适用场景:状态标志与双重检查锁定
volatile 最适合用作**轻量级的状态信号**,且该变量独立、无需与其他操作构成原子组。
立即学习“Java免费学习笔记(深入)”;
- 线程启停控制:
volatile boolean running = true;,其他线程通过轮询while(running)响应停止信号 - 单例模式中的双重检查锁定(DCL):
private static volatile Singleton instance;,防止指令重排序导致其他线程拿到未初始化完成的对象 - 发布不可变对象引用:用 volatile 发布已完全构造好的对象(配合 final 字段),确保其他线程看到一致状态
它不是“事件通知”,别误用为线程唤醒机制
volatile 不会唤醒阻塞线程,也不触发任何回调。如果线程在等待某个条件,仅靠 volatile 改变标志位,不会自动让等待线程从 wait()、sleep() 或 park() 中醒来。
- 错误做法:线程 A 设置
done = true(volatile),线程 B 在while(!done) Thread.sleep(10);—— 这是忙等,浪费 CPU - 正确做法:配合
wait()/notify()或LockSupport.park()/unpark(),volatile 仅用于共享状态判断 - 更推荐使用
CountDownLatch、Phaser、BlockingQueue等高级同步工具,它们内部封装了 volatile + 锁 + 条件队列

















