极高 进阶
CAS原理与ABA问题#
一句话答案#
CAS 比较并交换(Compare And Swap)是 CPU 原子指令,ABA 问题用版本号/AtomicStampedReference 解决。
核心要点
CAS(Compare And Swap):
- 三个操作数:内存位置 V(当前值)、期望值 A、新值 B
- 如果 V == A,则将 V 改为 B,返回 true;否则不修改,返回 false
- 整个操作是原子的(硬件层面保证)
底层实现:CPU 指令 cmpxchg(Compare and Exchange)
// Java AtomicInteger 的实现
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
// unsafe 调用 JNI 最终执行 CPU 的 cmpxchg 指令
// 多核 CPU 上加 lock 前缀,保证原子性javaCAS 的三个问题:
1. ABA 问题
- 值从 A → B → A,CAS 认为没变化,但中间其实变过
- 解决:
AtomicStampedReference(附加版本号/时间戳)
2. 自旋开销
- CAS 失败后循环重试(自旋),高并发下 CPU 空转严重
- 解决:限制自旋次数,或换用 LongAdder(分段 CAS)
3. 只能保证一个变量的原子性
- 需要原子操作多个变量时,CAS 无法直接解决
- 解决:
AtomicReference<Object>(将多个变量封装成对象),或换用 synchronized
面试回答(2分钟版)
CAS 即 Compare And Swap,是一种乐观锁思想的原子操作,包含三个操作数:内存值 V、期望值 A、新值 B,只有当 V 等于 A 时才将 V 更新为 B,否则不做任何修改。在 Java 中通过 Unsafe 类调用 JNI,最终执行 CPU 的 cmpxchg 指令,多核环境下加 lock 前缀保证原子性。AtomicInteger、AtomicLong 等原子类的底层都是基于 CAS 实现的。CAS 有三个常见问题:第一是 ABA 问题,值从 A 变成 B 又变回 A,CAS 会误认为没有变化,解决办法是用 AtomicStampedReference 附加版本号,每次修改版本号递增;第二是自旋开销,高并发下 CAS 失败会反复重试空耗 CPU,可以用 LongAdder 分段 CAS 来缓解;第三是只能保证单个变量的原子性,多变量的原子操作需要封装为对象用 AtomicReference 或者直接加锁。
追问与易错
追问方向:
- “CAS 底层怎么实现的?”→ Unsafe 类调用 CPU 的 cmpxchg 指令
- “ABA 问题有什么实际危害?”→ 链表/栈操作可能导致数据丢失
- “CAS 自旋失败一直重试怎么办?”→ 有自适应自旋,超过阈值升级锁
易错点:
- ❌ “CAS 是无锁的所以没有开销”——自旋消耗 CPU,竞争激烈时不如锁
- ❌ “ABA 问题不常见可以忽略”——在无锁栈/链表等场景会有实际问题