CAS(Compare and Swap)
一、是什么?
CAS(Compare and Swap)是一种无锁并发原子操作,用于在多线程环境下保证共享变量的原子性更新。其核心逻辑是:
“如果内存位置V的值等于预期值A,则将该位置更新为新值B;否则不更新并返回当前值。”
在Java中,CAS通过sun.misc.Unsafe
类的本地方法实现(如compareAndSwapInt
),但开发者通常使用java.util.concurrent.atomic
包下的工具类(如AtomicInteger
)间接操作。
二、解决什么问题
- 锁竞争开销:传统锁(如
synchronized
)在高并发时线程切换开销大,CAS通过硬件级原子指令避免锁竞争。 - 原子操作需求:解决多线程下
i++
等非原子操作的安全问题,无需加锁。 - 死锁风险:无锁机制规避了死锁可能性。
三、核心方法
在AtomicInteger
中的典型方法:
java
public final boolean compareAndSet(int expect, int update) // JDK标准API
public final int getAndIncrement() // 内部基于CAS实现自增
四、应用场景
- 计数器:如网站访问量统计(
AtomicLong
)。 - 状态标志:轻量级状态切换(如线程池状态)。
- 无锁数据结构:
ConcurrentHashMap
的桶节点更新、LinkedBlockingQueue
。 - 乐观锁:数据库版本号更新、分布式锁(如Redis的Lua脚本)。
五、Java示例
java
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
public static void main(String[] args) {
AtomicInteger count = new AtomicInteger(0); // 初始值为0
// 模拟10个线程并发自增
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
count.getAndIncrement(); // 内部使用CAS自增
}
}).start();
}
// 等待所有线程结束
try { Thread.sleep(1000); }
catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("最终结果: " + count.get()); // 正确输出10000
}
}
六、与锁的区别
特性 | CAS | 锁(如synchronized) |
---|---|---|
机制 | 无锁(硬件级原子指令) | 阻塞式锁 |
线程阻塞 | 无阻塞(自旋重试) | 可能阻塞线程 |
适用场景 | 低竞争、简单操作 | 高竞争、复杂临界区 |
性能 | 高并发下吞吐量更高 | 竞争激烈时性能下降明显 |
死锁风险 | 无 | 可能发生死锁 |
七、重要注意事项
- ABA问题:
- 问题:值从A→B→A,CAS误认为未被修改。
- 解决:JDK 8+ 使用
AtomicStampedReference
添加版本号。
javaAtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 0); ref.compareAndSet(100, 101, stamp, stamp + 1); // 检查值+版本号
- 自旋开销:高竞争时频繁重试消耗CPU资源。
- 单一变量限制:只能保证单个变量的原子性,复合操作需结合其他机制。
- JDK 8+增强:
LongAdder
替代AtomicLong
应对高竞争计数器场景(分段CAS)。CompletableFuture
内部使用CAS优化异步任务状态更新。
八、总结
CAS是Java无锁并发的基石,通过硬件指令实现高效原子操作,适用于计数器、状态标志等场景。其优势是高性能、无死锁,但需警惕ABA问题及自旋开销。JDK 8+提供了LongAdder
、StampedReference
等优化工具,开发者应结合场景选择CAS或锁机制。