CAS(Compare-And-Swap,比较并交换)
CAS(Compare-And-Swap,比较并交换) 是 Java 并发编程中实现无锁(lock-free)线程安全的重要机制,是 java.util.concurrent 包如 AtomicInteger、ConcurrentHashMap、ThreadPoolExecutor 等底层并发类的核心基础。
一、CAS 是什么?
CAS 是一种原子操作指令,它的作用是:如果内存中的值和预期值相同,则将其更新为新值;否则不做任何操作。
基本操作原理:
CAS(V, E, N)
V:内存中的变量(volatile)
E:期望值(Expected)
N:新值(New)
含义:如果 V == E,则 V ← N,否则什么都不做
二、CAS 在 Java 中的实现(Unsafe)
Java 中的 CAS 是通过 sun.misc.Unsafe 类封装的 compareAndSwapInt() / compareAndSwapLong() 等方法实现的,最终调用的是 CPU 提供的原子指令(如 x86 的 CMPXCHG)。
三、CAS 示例(以 AtomicInteger 为例)
AtomicInteger count = new AtomicInteger(0);
public void increment() {
while (true) {
int oldValue = count.get();
int newValue = oldValue + 1;
// 如果旧值没有被其他线程更改,则更新成功
if (count.compareAndSet(oldValue, newValue)) {
break;
}
// 否则自旋,继续重试
}
}
✅
compareAndSet()方法底层就是一个 CAS 原子操作。
四、CAS 的优点
| 优点 | 说明 |
|---|---|
| ✅ 无需加锁 | 提高并发性能,避免上下文切换 |
| ✅ 原子性强 | 由 CPU 保证的硬件级原子操作 |
| ✅ 高效 | 在竞争不严重的场景下性能优于锁 |
五、CAS 的三大问题及解决方案
1、ABA 问题
- 问题: CAS 判断的是值是否相等,如果值从 A → B → A,会误判为没变。
- 解决: 引入版本号机制,如
AtomicStampedReference。
AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 1);
2、自旋开销大
- 问题: 如果 CAS 一直失败,会不断重试(自旋),浪费 CPU。
- 解决:
- 控制重试次数
- 使用
LongAdder等分段方式降低冲突 - 在竞争严重场景用锁替代
3、只能保证单变量原子性
- 问题: CAS 只能保证一个变量的原子操作
- 解决:
- 使用锁保证多个变量的一致性
- 或使用原子引用(
AtomicReference)
六、CAS vs 锁(synchronized / ReentrantLock)
| 对比点 | CAS | 锁(synchronized) |
|---|---|---|
| 原子性保障 | CPU 指令层面 | JVM 保证 |
| 是否阻塞 | ❌ 非阻塞,自旋 | ✅ 阻塞 |
| 性能(低竞争) | ✅ 高性能 | 较慢(有上下文切换) |
| 性能(高竞争) | ❌ 自旋浪费资源 | ✅ 线程挂起等待,系统资源少占用 |
| 多变量原子性 | ❌ 不支持 | ✅ 支持 |
七、CAS 使用的类(JDK 并发包)
| 类名 | 使用 CAS 的字段 |
|---|---|
AtomicInteger | value |
AtomicReference | 引用对象 |
ConcurrentHashMap | Node.val、表初始化 |
ThreadPoolExecutor | ctl |
LongAdder | 分段累加器控制逻辑 |
八、CPU 层级实现
- CAS 的 CPU 指令:
CMPXCHG(x86 架构) - 原子性保障:通过总线锁或缓存一致性协议(MESI)
九、参考链接
十、总结记忆(口诀)
CAS 三元组,比较换更新;轻量不加锁,三大问题警惕;结合原子类,高效保线程。
更多详细内容请关注其他相关文章!