volatile 原理与内存屏障
「volatile 不是万能的,用不好反而会出问题」。这句话很多人听过,但真正理解 volatile 原理的人不多。volatile 的本质是内存屏障,而内存屏障是理解 Java 并发底层机制的关键。
volatile 的作用
保证可见性
保证有序性
关键:没有 volatile,步骤 1 和 2 可能被重排。
内存屏障
四种内存屏障
volatile 读写的内存屏障
volatile 写:
- StoreStore 屏障:禁止前面的普通写与 volatile 写重排
- volatile 写:将数据刷新到主内存
- StoreLoad 屏障:防止 volatile 写与后续的 volatile 读重排
volatile 读:
- LoadLoad 屏障:禁止前面的 volatile 读与普通读重排
- volatile 读:从主内存读取数据
volatile 的底层实现
x86 架构
在 x86 架构下:
- volatile 写使用
lock前缀或mfence指令 - volatile 读使用普通 mov 指令(x86 的 mov 本身是内存屏障)
CPU 缓存一致性
现代 CPU 使用 MESI(Modified-Exclusive-Shared-Invalid)协议保证缓存一致性。
volatile 的使用场景
场景一:状态标志
场景二:双重检查锁定
场景三:安全发布
volatile 的局限性
不保证原子性
验证:
正确做法:使用原子类
volatile vs synchronized
对比
选择原则
Happens-before 视角
volatile 的 happens-before 保证
volatile 写 happens-before volatile 读,这保证了:
- 写之前的普通写不会被重排到 volatile 写之后
- 读之后的普通读不会被重排到 volatile 读之前
- 写线程对共享变量的修改对读线程可见
性能影响
volatile 的性能成本
在现代 CPU 上,volatile 的性能影响通常可接受,但在高频竞争场景下仍需考虑。
本章总结
核心要点:
- volatile 保证可见性和有序性:不保证原子性
- 内存屏障:四种类型(LoadLoad/StoreStore/LoadStore/StoreLoad)
- volatile 写:StoreStore + StoreLoad 屏障
- volatile 读:LoadLoad + LoadStore 屏障
- 使用场景:状态标志、安全发布、双重检查锁定
- 不适用场景:需要原子性的操作(如 i++)
理解 volatile 的底层原理是深入 Java 并发的基础。下一节我们将讲解 synchronized 实现原理与优化。