volatile通过强制每次读取从主内存加载、每次写入立即刷回并使其他缓存失效,解决“修改不可见”问题;但它不保证复合操作原子性,也不能替代synchronized处理多步逻辑或状态协同。

volatile 关键字让变量的修改对其他线程“立刻可见”,核心在于绕过线程私有缓存,强制与主内存同步。
为什么普通变量会“看不见”修改
每个线程都有自己的工作内存(对应 CPU 寄存器或 L1/L2 缓存),读写共享变量时默认操作的是本地副本。线程 A 把 flag = true 写进自己缓存,但没刷回主内存;线程 B 一直从自己缓存读 flag,看到的始终是旧值 false——这就是典型的可见性丢失。
volatile 怎么解决“看不见”
它通过两个硬性约束打破缓存隔离:
- 每次读 volatile 变量,必须从主内存加载最新值,不许用工作内存里的缓存
- 每次写 volatile 变量,必须立即写回主内存,并触发缓存一致性协议(如 MESI),使其他 CPU 缓存中该变量所在缓存行失效
典型正确用法:状态开关
只用于“单次赋值 + 单次读取”的简单协同场景,比如控制线程生命周期:
private volatile boolean running = true;while (running) { /* 工作逻辑 */ }
running = false; // 其他线程立刻能感知到
这种写法安全,因为不涉及复合操作,也不依赖其他非 volatile 字段的状态。
容易踩的坑
- 误以为能保原子性:volatile int count; count++ 仍是三步操作,多线程下结果不可靠
- 混用非 volatile 字段判断:if (running && data.size() > 0),后者 size() 不可见,可能读到过期集合状态
- 用 volatile Boolean 包装类:自动拆箱可能空指针,且 null 值本身无同步语义
-
修饰引用类型只保地址可见:volatile List
list; list = new ArrayList(); —— 新对象地址可见,但 add() 后的内容是否可见,得看 List 实现是否线程安全
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











