从 CPU 缓存一致性的角度,volatile 的实现依赖于两个关键机制:
1. 内存屏障(Memory Barrier)
volatile 写入时,JVM 会插入 StoreStore 和 StoreLoad 屏障:
// volatile 写
StoreStore Barrier // 禁止上面的普通写与下面的volatile写重排序
volatile写操作
StoreLoad Barrier // 禁止volatile写与下面可能的volatile读/写重排序
volatile 读取时,插入 LoadLoad 和 LoadStore 屏障:
// volatile 读
volatile读操作
LoadLoad Barrier // 禁止下面的普通读与上面的volatile读重排序
LoadStore Barrier // 禁止下面的普通写与上面的volatile读重排序
2. 缓存一致性协议(MESI)
在 x86 架构上,volatile 写通常被翻译为带 lock 前缀的指令(如 lock addl $0x0, (%rsp))。这个 lock 前缀会:
- 锁定缓存行:将当前 CPU 缓存行的状态设为 Modified
- 触发缓存一致性协议:通过总线嗅探(Bus Snooping),使其他 CPU 缓存中该地址的缓存行失效(Invalid)
- 强制写回主存:确保修改对其他核心可见
CPU 0 写入 volatile 变量
↓
缓存行状态: M (Modified)
↓
总线广播失效消息
↓
CPU 1, 2, 3... 的缓存行变为 I (Invalid)
↓
其他 CPU 下次读取时必须从主存/其他缓存获取最新值
3.volatile vs synchronized 区别
| 维度 | volatile | synchronized |
|---|---|---|
| 原子性 | 仅保证单次读/写的原子性 | 保证临界区内所有操作的原子性 |
| 可见性 | ✓ 保证 | ✓ 保证 |
| 有序性 | 禁止指令重排序 | 禁止指令重排序 |
| 互斥性 | ✗ 无锁,不阻塞 | ✓ 排他锁,可能阻塞 |
| 实现层面 | 内存屏障 + 缓存一致性 | monitor 锁 + 内存屏障 |
4. 内存语义的关键差异
volatile 的问题——不保证复合操作的原子性:
volatile int count = 0;
// 线程A和B同时执行
count++; // 这不是原子操作!
// 实际是: 读取 → 加1 → 写入(三步)
两个线程可能同时读到 count=0,各自加1后写入,结果是1而非2。
synchronized 保证互斥:
synchronized(lock) {
count++; // 同一时刻只有一个线程能执行
}
```
### 内存语义模型对比
```
volatile 写 → 相当于 synchronized 退出(释放锁)
volatile 读 → 相当于 synchronized 进入(获取锁)
但 volatile 缺少 synchronized 的「临界区」概念
5. 适用场景
用 volatile:
- 状态标志位(如
volatile boolean running) - 单次写入后被多线程读取的配置
- Double-Check Locking 中防止指令重排序
用 synchronized:
- 需要原子性的复合操作(读-改-写)
- 需要保护多个相关变量的一致性
103

被折叠的 条评论
为什么被折叠?



