volatile 的底层实现:lock 指令和内存屏障¶
一、lock 指令¶
volatile 写操作,JVM 会生成这样的字节码:
lock 前缀的作用:
- 锁缓存行(不是总线),用 MESI 协议让其他核缓存行失效。
- 把当前核缓存写回内存(或写无效化请求到其他核)。
- 禁止指令重排:lock 前的操作不能排到后面。
二、内存屏障(Memory Barrier)¶
JMM 定义了四种屏障:
| 屏障 | 作用 |
|---|---|
| LoadLoad | 读 1 → LoadLoad → 读 2:保证读 1 先于读 2 |
| StoreStore | 写 1 → StoreStore → 写 2:保证写 1 先于写 2 |
| LoadStore | 读 1 → LoadStore → 写 2:保证读 1 先于写 2 |
| StoreLoad | 写 1 → StoreLoad → 读 2:最强,写后读要等写刷出去 |
三、volatile 插入的屏障¶
写 volatile¶
读 volatile¶
四、为什么需要 StoreLoad¶
StoreLoad 最贵,它要保证:写 volatile 后,后面的读必须看到最新值。
x86 上,lock 指令本身就实现了 StoreLoad 的语义。其他架构(ARM、PowerPC)需要单独的屏障指令。
五、和 MESI 的关系¶
- lock 指令触发 MESI:把缓存行设为 M 并写回,其他核变 I。
- 屏障禁止 CPU 和编译器的重排。
两者配合,volatile 的可见性和有序性才有硬件基础。
面试加分
- x86 是强内存模型,只有 StoreLoad 需要显式屏障,其他屏障 x86 自动满足。
- ARM 是弱内存模型,所有屏障都需要显式指令。