主题
概念卡片:CAS 乐观锁与原子类
一句话机制
CAS(Compare And Swap,比较并交换)是乐观锁思想:更新前先判断变量是否还是自己当初看到的值,是就更新、不是就失败重试;底层靠 CPU 原子指令 lock cmpxchg 保证「比较+交换」不可分割。 它是 AtomicInteger、ConcurrentHashMap、AQS 等一切 JUC 无锁并发的基石。
核心思想
CAS(V, A, B):当内存值 V == 期望旧值 A 时,把 V 更新为 B,否则失败count++ 不是原子操作(读→加1→写回),两线程同时自增会丢更新。CAS 不阻塞线程,而是失败就重试——这就是「CAS + 自旋」。
java
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next)) return next;
}
}底层实现
- Java 层:
sun.misc.Unsafe.compareAndSwapInt(...) - CPU 层:x86 的
lock cmpxchg(lock前缀在多核下锁总线,保证指令执行期间其他核不能访问该内存地址)
volatile 与 CAS 的分工:
volatile保证「看得见最新值」,CAS 保证「比较并更新这个动作是原子的」。AtomicInteger 内部是private volatile int value+ CAS 更新。
CAS 三大经典问题
| 问题 | 现象 | 解法 |
|---|---|---|
| ABA 问题 | T2 把 A→B→A,T1 的 CAS 误以为没变过 | AtomicStampedReference(值+版本号一起比) |
| 自旋开销 | 高竞争下大量线程 CAS 失败空转耗 CPU | LongAdder(分散热点)、分段、必要时用锁 |
| 单变量局限 | 只能保证一个变量的原子性 | 封装不可变对象 + AtomicReference 整体替换,或直接加锁 |
ABA 详解
ABA:值从 A 改成 B 又改回 A,CAS 只比较值,发现还是 A 就更新成功,但中间已经变过两次。解决靠版本号:A(1) → B(2) → A(3),值相同但版本号不同,AtomicStampedReference 同时比较值和版本号。
LongAdder:高并发计数优化
AtomicLong 所有线程竞争同一个 value,并发越高 CAS 失败越多。LongAdder 用 base + Cell[] 分散热点:竞争不激烈更新 base,激烈时分散到不同 Cell,求和时 sum = base + Σcell。
- 低并发 + 需准确即时值 →
AtomicLong - 高并发 + 统计计数(访问量/指标)→
LongAdder(sum()非强一致瞬时值)
CAS vs 锁的选型
| 维度 | CAS(乐观锁) | synchronized / ReentrantLock |
|---|---|---|
| 适合 | 操作简单、变量少、冲突不严重、想免阻塞 | 临界区复杂、多变量、竞争激烈、需等待唤醒 |
| 场景 | 自增计数、状态 0→1、引用替换、简单累加 | 转账、库存扣减+日志、多字段更新、生产者消费者 |
常见误解(避坑)
- ❌ "AtomicReference 比较的是对象内容"。→ 比较的是引用是否相同(
==),不是equals;对象内容变了但引用没变,CAS 感知不到。 - ❌ "CAS 无锁就永远比锁快"。→ 竞争激烈时自旋空转耗 CPU,性能可能反而不如阻塞锁;「无锁」不是银弹,要按场景选。
- ❌ "volatile 能替代 CAS"。→ 两者各管一半:volatile 管可见性、CAS 管原子更新,
count++光有 volatile 仍会丢更新。 - ❌ "LongAdder.sum() 是精确瞬时值"。→ 它是各 Cell 的弱一致求和,适合统计场景,不适合需要精确值的业务判断。
关联
- 原始资料:CAS详解
- 总览:并发编程技术栈总览
- 相关卡:概念卡片:synchronized锁升级与Monitor · 概念卡片:AQS同步器框架
- 域地图:A00-百科/Java后端/Java后端