volatile通过内存屏障与MESI协议协同实现可见性:写操作插入StoreStore+StoreLoad屏障,强制刷新并使其他核心缓存失效;读操作插入LoadLoad+LoadStore屏障,强制从主内存或有效缓存重新加载最新值,共同建立happens-before关系。

Java 中 volatile 关键字通过强制线程绕过工作内存、直连主内存,并配合底层硬件机制,确保一个线程对变量的修改能被其他线程“及时感知”。它不靠锁,也不阻塞线程,而是从内存访问行为和指令执行顺序两个层面切入。
写操作:立即刷新到主内存并触发缓存失效
当线程写入一个 volatile 变量时,JVM 会在对应字节码(如 putstatic)后插入写屏障(Store Barrier)。该屏障会:
- 强制将新值从 CPU 寄存器或高速缓存刷出,进入 L3 缓存或写缓冲区;
- 借助
lock前缀指令(x86 平台)触发 MESI 缓存一致性协议,使其他 CPU 核心上该变量所在的缓存行标记为 “Invalid”; - 不是直接写主存——现代 CPU 从不跳过缓存写主存,但缓存失效后,其他线程下次读取就必须重新加载最新值。
读操作:每次强制从主内存(或有效缓存)重新加载
当线程读取一个 volatile 变量时,JVM 会在读指令前插入读屏障(Load Barrier),从而:
- 禁止编译器和 CPU 将该读操作重排序到屏障之前的任何读写之后;
- 丢弃寄存器或工作内存中的旧副本,要求从当前有效的缓存层级(通常是 L1/L2,若已失效则穿透到主内存)重新加载;
- 确保读到的值是已被其他线程“提交”的最新结果——哪怕有短暂延迟,一旦读到,就代表整个写前操作对当前线程可见(happens-before 语义)。
为什么普通变量做不到?根源在 JMM 和硬件分工
Java 内存模型(JMM)允许每个线程维护变量副本,普通变量的读写可完全停留在工作内存中,无需同步到主内存。而 CPU 多核架构下,各核心缓存独立,没有协调机制就会出现“各自看到不同值”的情况。volatile 的作用正是在 JMM 规则内,用内存屏障 + 缓存一致性协议,在不加锁的前提下补上这个断点。
立即学习“Java免费学习笔记(深入)”;
关键提醒:可见性 ≠ 原子性
volatile 只保证单次读或单次写的可见与有序,不干预复合操作:
-
flag = true;✅ 可见; -
count++;❌ 不可见+不原子(等价于读-改-写三步,中间可能被抢占); -
if (ready && data != null)❌ 即使两个变量都 volatile,也不能保证这两者之间的逻辑原子性。
需要原子性或临界区保护时,必须搭配 synchronized、Lock 或 AtomicInteger 等工具。


















