CAS是一种由CPU提供的原子指令,用于在不使用锁的情况下实现变量的原子更新,其核心是“比较并交换”:仅当当前值等于预期值时才更新为新值,并保证整个操作不可分割。

CAS 是什么:不是锁,但能实现原子更新
CAS(Compare-And-Swap)不是 Java 语言关键字,也不是某个类,而是一种由 CPU 提供的原子指令,在 Java 中通过 Unsafe.compareAndSwapInt 等方法暴露出来,是 java.util.concurrent.atomic 包(如 AtomicInteger、AtomicReference)的底层支撑机制。
它的语义很简单:仅当当前变量的值等于预期值时,才将其更新为新值,并返回是否成功。整个过程不可分割——要么全部完成,要么完全不发生。
CAS 的 Java 层调用路径:从 AtomicXxx 到 Unsafe
以 AtomicInteger.incrementAndGet() 为例,它最终会调用:
Unsafe.compareAndSwapInt(this, valueOffset, expectedValue, newValue)- 其中
valueOffset是通过Unsafe.objectFieldOffset获取的字段内存偏移量 -
this是当前对象(如AtomicInteger实例),不是直接操作栈上变量
注意:JDK 9+ 中 Unsafe 被逐步限制,VarHandle 和 MethodHandle 成为更推荐的替代方案,但底层仍依赖相同 CPU 指令。
立即学习“Java免费学习笔记(深入)”;
CAS 的硬件级实现:CPU 指令 + 缓存一致性协议
在 x86 平台上,CAS 对应的是 cmpxchg 指令;在 ARM 上则是 ldrex/strex 搭配使用。关键点在于:
- 该指令本身由 CPU 硬件保证原子性,不需要操作系统介入
- 多核环境下,靠 MESI 协议确保缓存行状态同步——执行 CAS 前会先将目标内存地址所在缓存行置为 Exclusive 或 Modified 状态
- 如果其他核心正在修改同一缓存行,当前 CAS 会失败(返回 false),这就是“自旋重试”的来源
这意味着 CAS 不是无代价的:频繁失败会导致大量重试和缓存行争用(false sharing),反而比锁更慢。
CAS 的三个经典问题:ABA、循环时间长、只能保证单变量
这是实际编码中最容易踩坑的地方:
-
ABA 问题:变量值从 A → B → A,CAS 认为没变就更新成功。典型场景是链表节点复用。解决方式是加版本号,Java 提供了
AtomicStampedReference -
自旋开销大:在高竞争下,
getAndIncrement()可能循环几十甚至上百次才成功。此时应考虑分段锁(如LongAdder)或退化为锁 -
只能原子更新一个变量:无法直接对两个字段(如账户余额 + 冻结金额)做原子读-改-写。需要封装为
AtomicReference或改用StampedLock
没有银弹。看到 AtomicXxx 就以为“一定比 synchronized 快”,是很多线上性能问题的起点。











