volatile读操作强制从主内存加载最新值并插入loadstore屏障防止重排序,写操作强制刷回主内存并插入storeload屏障确保之前所有操作对其他线程可见,从而建立happens-before关系保证可见性与有序性。

volatile 变量的读写操作直接影响线程间对共享状态的感知能力,核心在于强制绕过线程本地缓存、直连主内存,并借助内存屏障约束执行顺序。
读操作:每次从主内存“重新加载”
当一个线程读取 volatile 变量时,JVM 会确保该操作不使用工作内存中的旧副本,而是直接从主内存读取最新值。这避免了因缓存未及时同步导致的“读到过期值”问题。
- 即使该变量刚被其他线程修改过,且尚未刷新到主内存,volatile 读也会触发一次主内存重载
- 同时,JVM 在 volatile 读之前插入“读后写”(LoadStore)屏障,防止它前面的读/写操作被重排序到该读之后
- 典型场景:轮询标志位(如 while (!done)),没有 volatile 就可能永远卡在旧值上
写操作:立即刷新到主内存
对 volatile 变量的写入,会强制将当前线程工作内存中的新值写回主内存,而非暂存于寄存器或缓存中。其他线程后续的 volatile 读就能看到这个更新。
- 写操作完成后,JVM 插入“写后读”(StoreLoad)屏障,确保该写之前的全部内存操作(包括普通变量写)都已对其他线程可见
- 这种“写前所有操作可见”的特性,是 volatile 支持简单状态发布(如初始化完成标记)的关键
- 例如:initialized = true(volatile)之前设置的 data = 42,对后续读到 initialized 的线程也一定可见
读写组合构成 happens-before 关系
Java 内存模型规定:对同一个 volatile 变量,一个线程的写操作 happens-before 另一个线程对该变量的读操作。这是 JMM 提供的明确可见性保证。
- 它不是靠轮询或延迟实现的,而是由 JVM 在字节码层面插入内存屏障指令,由 CPU 硬件配合完成
- 这种关系可传递:如果 A happens-before B,B happens-before C,则 A happens-before C
- 注意:它只保证该 volatile 变量本身及其写操作前的其他写操作可见,不扩展到无关变量
和普通变量的本质区别
普通变量的读写完全可能停留在工作内存中,JMM 不保证何时同步;volatile 则用内存屏障“锁住”读写路径,让主内存成为唯一可信源。
- 普通变量:线程 A 写 → 可能滞留缓存 → 线程 B 读 → 读到旧值(常见于无同步的标志位)
- volatile 变量:线程 A 写 → 强制刷主存 → 线程 B 读 → 强制拉主存 → 一定看到新值
- 代价是性能略低(更多内存屏障、更少编译器优化),但远低于加锁
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











