面试题:volatile 实现原理?它和 synchronized的区别?

从 CPU 缓存一致性的角度,volatile 的实现依赖于两个关键机制:

1. 内存屏障(Memory Barrier)

volatile 写入时,JVM 会插入 StoreStoreStoreLoad 屏障:

// volatile 写
StoreStore Barrier  // 禁止上面的普通写与下面的volatile写重排序
volatile写操作
StoreLoad Barrier   // 禁止volatile写与下面可能的volatile读/写重排序

volatile 读取时,插入 LoadLoadLoadStore 屏障:

// 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 区别

维度volatilesynchronized
原子性仅保证单次读/写的原子性保证临界区内所有操作的原子性
可见性✓ 保证✓ 保证
有序性禁止指令重排序禁止指令重排序
互斥性✗ 无锁,不阻塞✓ 排他锁,可能阻塞
实现层面内存屏障 + 缓存一致性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:

  • 需要原子性的复合操作(读-改-写)
  • 需要保护多个相关变量的一致性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值