CAS是CPU级原子指令,通过Unsafe调用cmpxchg实现“V==A时更新为B”的无锁操作,解决volatile无法覆盖的i++等复合操作原子性问题,但存在ABA、自旋开销和单变量限制三大缺陷。

Java CAS 是大厂面试绕不开的硬核考点,不是背定义就能过关,得讲清原理、说对场景、指出缺陷、还能对比选型。
CAS 是什么:别只说“比较并交换”
它是一条 CPU 级别的原子指令(Compare-and-Swap),不是 Java 语法,而是 JVM 借助 Unsafe 类调用操作系统底层实现的原语。核心参数只有三个:内存地址 V、预期值 A、新值 B。只有当 V 处当前值等于 A 时,才把 V 更新为 B,整个过程不可中断。
面试时如果只答“它保证原子性”,大概率被追问。要主动补一句:它解决的是 volatile 无法覆盖的原子性问题——比如 i++ 这种“读-改-写”复合操作。
为什么需要 CAS:从 i++ 的线程不安全说起
直接手写一段多线程自增代码,比空讲更有说服力:
立即学习“Java免费学习笔记(深入)”;
-
int count = 0;在 5 个线程里各执行 1000 次count++→ 结果几乎肯定 < 5000 - 换成
AtomicInteger count = new AtomicInteger(0);同样操作 → 结果稳定为 5000
原因在于:普通 int 的 count++ 实际拆成三步(读值→+1→写回),中间可能被其他线程打断;而 AtomicInteger.getAndIncrement() 底层靠 CAS 循环重试,直到成功为止。
CAS 的三大典型问题,必须能答全
大厂爱考“缺点”,因为能看出你是否真用过、调过、踩过坑:
-
ABA 问题:V 从 A→B→A,CAS 误判“没变”而成功更新。解法:用
AtomicStampedReference加版本戳 -
自旋开销:竞争激烈时,线程反复读-比-换,空耗 CPU。解法:JDK8 后
LongAdder改用分段累加,降低单点竞争 - 只能保证单变量原子性:无法直接处理多个字段的复合操作(如账户转账:扣 A、加 B)。解法:配合锁,或设计成无状态的乐观更新(如带 version 字段的 update where version = ?)
和 synchronized 怎么选:看场景,不站队
别张口就说“CAS 更快”。要结合数据特征和并发模式判断:
- 适合 CAS:低冲突、简单状态变更,比如开关标志位、计数器、对象引用替换
- 适合 synchronized:高竞争、临界区复杂、需阻塞等待,比如批量库存扣减、跨表事务逻辑
- 补充一句:
ReentrantLock的tryLock()其实也是 CAS 思想的延伸——它本质是用 CAS 尝试获取 state 状态位


















