CAS(Compare And Swap) 是一种 无锁并发控制技术,用于实现线程安全的原子操作。它的核心思想是:先比较内存中的值是否与预期一致,若一致则更新,否则重试。以下是其核心要点:
一、CAS 核心流程
-
读取当前值:线程从内存中读取变量的当前值(称为 预期值,Expected Value)。
-
计算新值:基于当前值计算出新值。
-
比较并交换:
- 如果内存中的当前值等于预期值 → 更新为新值。
- 如果不等于 → 放弃更新(可能重试)。
示例:
假设变量 value 初始为5,线程A和线程B都想将其加1:
- 线程A读取
value=5,计算新值6 → 执行CAS:内存值仍为5 → 更新为6(成功)。 - 线程B读取
value=6,计算新值7 → 执行CAS:内存值为6 → 更新为7(成功)。
二、Java中的CAS实现
-
Atomic类(如
AtomicInteger) :
Java提供原子类,底层通过Unsafe类调用CPU指令实现CAS。AtomicInteger count = new AtomicInteger(5); count.incrementAndGet(); // 内部通过CAS实现自增 -
Unsafe类:
Java通过Unsafe类直接操作内存,调用本地方法(如compareAndSwapInt)。public final native boolean compareAndSwapInt( Object obj, long offset, int expected, int newValue ); -
硬件支持:
- x86架构的CPU指令
CMPXCHG直接支持CAS操作。 - ARM架构通过
LDREX/STREX指令实现类似功能。
- x86架构的CPU指令
三、CAS的优缺点
| 优点 | 缺点 |
|---|---|
| 无锁,减少线程阻塞和切换开销 | ABA问题(后文详解) |
| 高并发场景性能优于锁 | 自旋可能导致CPU资源浪费 |
| 简单轻量,适合简单原子操作 | 仅支持单一变量,复杂操作需组合 |
四、ABA问题及解决方案
-
ABA问题:
- 线程A读取变量值为
A→ 被线程B修改为B→ 又被线程B改回A。 - 线程A执行CAS时,认为值未变化,继续操作 → 可能导致逻辑错误。
- 线程A读取变量值为
-
解决方案:
-
版本号机制:每次修改增加版本号,CAS同时检查值和版本号。
-
AtomicStampedReference:Java提供的带版本号的原子引用类。
java
复制
AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(5, 0); ref.compareAndSet(5, 6, 0, 1); // 检查值5和版本号0,更新为值6和版本号1
-
五、CAS的适用场景
- 计数器:如
AtomicInteger的自增操作。 - 无锁数据结构:如无锁队列、栈。
- 状态标记:如线程池的状态控制。
- 资源分配:如数据库连接池的并发分配。
六、对比锁机制
| 对比项 | CAS | 锁(synchronized/Lock) |
|---|---|---|
| 线程阻塞 | 无阻塞(自旋重试) | 可能阻塞(等待锁释放) |
| 性能 | 高并发下性能更优 | 高竞争下性能下降 |
| 适用操作 | 简单原子操作 | 复杂同步逻辑 |
| 内存开销 | 低 | 锁对象和队列占用内存 |
七、代码示例
public class CASExample {
private static AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) {
// 多线程并发自增
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
counter.incrementAndGet(); // CAS实现原子自增
}
}).start();
}
// 等待所有线程完成
try { Thread.sleep(2000); } catch (InterruptedException e) {}
System.out.println("Final count: " + counter.get()); // 输出10000
}
}
总结
CAS是并发编程的基石:
- 通过硬件支持的原子操作实现无锁并发。
- 适合简单原子操作,但需注意ABA问题和自旋开销。
- 在Java中广泛应用于
Atomic类、ConcurrentHashMap等并发容器。
口诀:
「CAS无锁真高效,比较交换是核心
Atomic类里显身手,自旋重试要记牢
ABA问题版本号,高并发下性能高!」