一、CAS 原理
1. 核心思想
CAS 是一种无锁的原子操作机制。它的核心思想是:我认为值应该是 A,如果是,那我就把它改成 B;如果不是 A(说明被别人改过了),那我就不修改,然后可以选择重试或放弃。
这个操作是作为一条CPU 硬件指令实现的(在 x86 架构上是 CMPXCHG 指令),因此它能保证原子性,不会被线程调度打断。
2. 操作模型
CAS 操作涉及三个操作数:
- 内存位置(V)
- 预期的原值(A)
- 新值(B)
伪代码逻辑如下:
if (V == A) {
V = B;
return true;
} else {
return false;
}
但关键是,整个比较和交换的过程是一个不可分割的原子操作。
3. 工作流程
当一个线程想要更新一个变量时,它会:
- 获取当前内存中的值,作为期望值
A。 - 计算出新值
B。 - 执行 CAS 指令,判断当前内存中的值是否还是
A。- 如果是,说明没有其他线程修改过,成功将值更新为
B。 - 如果不是,说明值已被其他线程修改,本次更新失败。线程通常会重试整个操作(获取新的当前值,计算新值,再次执行 CAS),直到成功为止。这种重试行为就是常见的自旋。
- 如果是,说明没有其他线程修改过,成功将值更新为
4. 优点与缺点
- 优点:
- 高性能:避免了重量级锁(如
synchronized)带来的线程阻塞、唤醒和上下文切换的开销,在竞争不激烈的场景下性能极高。 - 避免死锁:由于是无锁操作,从根本上避免了死锁问题。
- 高性能:避免了重量级锁(如
- 缺点:
- ABA 问题:CAS 只检查值是否变化,但如果一个值从 A 变成 B,又被改回 A,CAS 会误以为它没变。解决方案是使用版本号或标记(如
AtomicStampedReference)。 - 自旋开销:在高竞争环境下,如果线程一直失败重试,会长时间占用 CPU,消耗资源。
- 只能保证一个共享变量的原子操作:对于多个共享变量,CAS 无法保证原子性。但可以将它们合并成一个对象,使用
AtomicReference来保证原子性。
- ABA 问题:CAS 只检查值是否变化,但如果一个值从 A 变成 B,又被改回 A,CAS 会误以为它没变。解决方案是使用版本号或标记(如
二、Java 中的 CAS 用法
在 Java 中,你不能直接使用 CPU 指令。CAS 的能力是通过 sun.misc.Unsafe 类中的本地(Native)方法提供的。但通常,我们不会直接使用 Unsafe,而是使用 JDK 在 java.util.concurrent.atomic 包下为我们封装好的原子类。

