Skip to content
极高进阶

一句话答案

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
// Java AtomicInteger 的实现
public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
// unsafe 调用 JNI 最终执行 CPU 的 cmpxchg 指令
// 多核 CPU 上加 lock 前缀,保证原子性

CAS 的三个问题:

1. ABA 问题

  • 值从 A → B → A,CAS 认为没变化,但中间其实变过
  • 解决:AtomicStampedReference(附加版本号/时间戳)

2. 自旋开销

  • CAS 失败后循环重试(自旋),高并发下 CPU 空转严重
  • 解决:限制自旋次数,或换用 LongAdder(分段 CAS)

3. 只能保证一个变量的原子性

  • 需要原子操作多个变量时,CAS 无法直接解决
  • 解决:AtomicReference<Object>(将多个变量封装成对象),或换用 synchronized
追问与易错

追问方向:

  • CAS 底层怎么实现的?(Unsafe 类调用 CPU 的 cmpxchg 指令)
  • ABA 问题有什么实际危害?(链表/栈操作可能导致数据丢失)
  • CAS 自旋失败一直重试怎么办?(有自适应自旋,超过阈值升级锁)

易错点:

  • ❌ "CAS 是无锁的所以没有开销"——自旋消耗 CPU,竞争激烈时不如锁
  • ❌ "ABA 问题不常见可以忽略"——在无锁栈/链表等场景会有实际问题

💡 记忆锚点

CAS 是"先看后改"的原子操作:我记住你是 A,改的时候发现还是 A 才动手,否则从头来。ABA 坑在于东西被换走又换回来,你以为没变其实变过——加个版本号(AtomicStampedReference)就像给每次修改盖个章,章号对不上就知道动过手脚。