CAS详解
1. CAS 核心概念
- 定义:Compare And Swap(比较并交换),一种无锁并发控制技术。
- 核心逻辑:
内存值 V,预期值 A,新值 B。 当且仅当 V == A 时,将 V 更新为 B,否则不操作。 整个操作由 CPU 保证原子性。
- 原子性保证:依赖 CPU 的
cmpxchg
指令(多核下通过总线锁定或缓存锁实现)。
2. CAS 底层实现
-
Unsafe 类
- 作用:Java 通过
sun.misc.Unsafe
类直接操作内存,调用本地方法(Native Method)实现 CAS。 - 关键方法:
compareAndSwapInt()
,compareAndSwapLong()
等。 - 示例:
AtomicInteger
的incrementAndGet()
底层通过循环 CAS 实现:public final int incrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, 1) + 1; }
- 作用:Java 通过
-
CPU 指令
- cmpxchg 指令:多核环境下通过缓存锁(MESI 协议)或总线锁保证原子性。
3. CAS 在 JUC 中的应用
-
原子类(Atomic Classes)
- 常用类:
AtomicInteger
,AtomicLong
,AtomicReference
。 - 核心方法:
compareAndSet(expected, newValue)
。 - 优化类:
LongAdder
(分散热点,减少 CAS 竞争)。
- 常用类:
-
AQS(AbstractQueuedSynchronizer)
- 同步状态管理:通过 CAS 修改
volatile int state
实现锁的获取与释放。 - CLH 队列:CAS 将竞争失败的线程封装为 Node 插入队列尾部。
- 同步状态管理:通过 CAS 修改
-
并发容器
- ConcurrentHashMap:插入桶节点时使用 CAS 避免锁竞争。
- CopyOnWriteArrayList:写操作通过 CAS 复制新数组保证原子性。
4. CAS 的缺陷与解决方案
问题 | 原因 | 解决方案 |
---|---|---|
ABA 问题 | 值从 A → B → A ,CAS 无法感知中间变化 | 使用 AtomicStampedReference (版本号)或 AtomicMarkableReference 。 |
自旋开销 | 高并发下 CAS 失败导致 CPU 空转 | 改用 LongAdder (分散热点)或退避策略(指数退避)。 |
单变量限制 | 无法保证多个变量的原子性 | 合并变量(封装为对象)或使用锁。 |
5. CAS vs 锁机制
维度 | CAS | synchronized/Lock |
---|---|---|
锁类型 | 无锁(乐观锁) | 悲观锁 |
性能 | 高(无上下文切换) | 低(锁竞争时性能差) |
适用场景 | 简单原子操作、低竞争 | 复杂逻辑、高竞争 |
编程复杂度 | 需处理 ABA 问题和自旋 | 简单(自动释放锁) |
6. 实战代码示例
-
AtomicInteger 自增
AtomicInteger count = new AtomicInteger(0); count.incrementAndGet(); // 底层通过 CAS 实现
-
解决 ABA 问题
AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0); int stamp = ref.getStamp(); ref.compareAndSet("A", "B", stamp, stamp + 1); // 更新值并递增版本号
-
手写自旋锁
public class SpinLock {private AtomicReference<Thread> owner = new AtomicReference<>();public void lock() {Thread current = Thread.currentThread();while (!owner.compareAndSet(null, current)) {} // CAS 自旋}public void unlock() {owner.compareAndSet(Thread.currentThread(), null);} }
7. 最佳实践
- 优先使用原子类:如
AtomicInteger
替代synchronized
计数器。 - 避免长时间自旋:设置最大重试次数或改用锁。
- 高竞争场景优化:使用
LongAdder
代替AtomicLong
。 - 敏感数据加版本号:如订单状态变更使用
AtomicStampedReference
。
总结
- CAS 是 JUC 的基石:通过无锁化实现高效并发,但需处理 ABA 问题和自旋开销。
- 适用场景:简单原子操作(计数器、标志位)、低竞争环境。
- 慎用场景:复杂多变量操作、高竞争环境(改用锁或
LongAdder
)。