volatile变量通过直通主内存、触发缓存一致性协议和插入内存屏障,确保状态的可见性与有序性,但仅保障单次读写操作,不保证复合操作原子性。

volatile 变量在 JMM 中传递状态,核心是打破线程工作内存的缓存依赖,强制所有读写都与主内存同步,并借助内存屏障约束指令顺序。
直通主内存:每次读写都绕过工作内存
普通变量可能长期停留在线程的工作内存(如 CPU 缓存或寄存器)中,其他线程看不到更新。volatile 改变了这个行为:
- 读操作:线程每次读 volatile 变量时,必须从主内存重新加载,不复用本地缓存值
- 写操作:线程每次写 volatile 变量后,必须立即将新值刷新回主内存,不延迟、不合并
- 效果:一个线程写入后,其他线程下一次读就能拿到最新值——状态通过主内存“显式中转”
触发缓存一致性协议:让其他核失效旧副本
仅刷新主内存还不够,CPU 缓存之间需要协同。volatile 写操作会插入写屏障(Write Barrier),触发底层缓存一致性协议(如 MESI):
- 使其他 CPU 核心中该变量所在缓存行标记为无效(Invalid)
- 下次其他线程读该变量时,因缓存失效,必须重新从主内存加载
- 这解决了“改了但对方还在用旧缓存”的典型可见性问题
插入内存屏障:禁止相关指令重排序
volatile 不仅管数据传递,还管执行顺序。JVM 会在 volatile 读写前后插入内存屏障:
- volatile 写前插入 StoreStore 屏障:确保上面的普通写先于 volatile 写完成
- volatile 写后插入 StoreLoad 屏障:防止下面的读写被重排到 volatile 写之前
- volatile 读后插入 LoadLoad 和 LoadStore 屏障:保证该读不会被重排到前面或后面的读写之前
- 这样就建立了“写 volatile → 其他线程读 volatile”的 happens-before 关系,状态传递有了顺序保障
只保单次操作,不延伸语义
volatile 的状态传递能力有明确边界:
- ✅ 有效:flag = true、if (flag)、obj = new Object()(配合 DCL)
- ❌ 无效:count++(读-改-写三步)、if (flag && data != null)(data 不受保护)、list.add(x)(volatile List 只保引用可见,不保内部元素)
- ⚠️ 注意:它不锁资源、不阻塞线程、不保证复合操作原子性——状态传递是“轻量级接力”,不是“全程护送”
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











