volatile通过读写屏障和缓存一致性协议保证可见性与有序性,但不保证原子性、不提供互斥,仅适用于状态标志、单次赋值等简单场景。

volatile 通过强制线程每次读都从主内存加载、每次写都立即刷回主内存,并配合底层缓存一致性机制,让一个线程对变量的修改能被其他线程“及时感知”——不是毫秒级推送,而是确保“一旦看到,就是最新值”。
它靠三件事协同工作
• 写操作加写屏障:线程修改 volatile 变量时,JVM 在对应汇编指令后插入写屏障(如 x86 的 lock addl),强制把新值从 CPU 缓存刷出,并触发 MESI 协议将其他 CPU 上该缓存行标记为“无效”;
• 读操作加读屏障:线程读取 volatile 变量前,插入读屏障,禁止重排序,且要求必须放弃工作内存中的旧副本,重新从主内存(或已更新的本地缓存)加载;
• 缓存一致性协议兜底:CPU 核心通过总线嗅探监听到缓存行失效通知,自动丢弃旧值;下次读取时只能去主内存或 L3 缓存获取最新提交的结果。
它不做什么,同样关键
• 不直接写主内存:现代 CPU 从不绕过缓存直写主存,volatile 的“刷出”是通过缓存行失效+重载实现的;
• 不保证“立刻被读到”:线程 B 可能短暂仍读到旧值(因缓存失效有微小延迟),但只要它读到了新值,就代表该写操作已完成,且写之前的所有内存操作对它也可见(happens-before 语义);
• 不阻止其他线程同时写:volatile 不是锁,多个线程可并发写同一个 volatile 变量,它只确保每个写的结果最终能被别人看见,而非互斥执行。
典型正确用法与常见误用
✅ 安全场景:
• 状态标志位:如 private static volatile boolean shutdownRequested = false;
• 单次赋值引用:如双重检查单例中的 private static volatile Singleton instance;
• 简单赋值状态码:如 status = DONE;(不能是 status++ 或 if (flag && count > 0))
❌ 高危误用:
• 对 volatile 变量做自增(count++)、复合判断(依赖多个 volatile 变量的 if 条件)、或对象内部字段更新(config.timeout = 5000;);
• 修饰局部变量、方法参数或非成员字段——语法错误,编译不过;
• 以为加了 volatile 就不用 synchronized——它解决不了竞态条件,只解决“看不看得见”的问题。
和没加 volatile 对比,差别在哪
没加 volatile 时,JIT 编译器可能把变量优化进寄存器,线程反复读寄存器副本;工作内存中的值长期不与主内存同步,导致“主线程永远等不到子线程设的 flag”。加上之后,每次读写都绕过寄存器和缓存旧值,形成一条轻量但确定的通信通道——代价极小,效果明确。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











