cas是cpu指令级原子操作,通过cmpxchg等硬件指令实现“读-比-写”三步不可分割;它非java语法,由unsafe封装调用,存在aba问题、循环开销大、不支持多变量复合操作等局限。

CAS 是什么:不是锁,是 CPU 指令级别的原子操作
CAS(Compare-And-Swap)不是 Java 语言层的语法或类,而是 Unsafe.compareAndSwapInt 这类方法背后调用的 CPU 原语(如 x86 的 cmpxchg)。它通过一条硬件指令完成「读取内存值 → 比较是否等于预期值 → 若是则写入新值」三步的原子性,中间不会被线程调度打断。
Java 中所有基于 CAS 的并发工具(如 AtomicInteger、ConcurrentHashMap 的插入逻辑)都依赖 Unsafe 类封装的这些底层调用。你无法直接 new Unsafe,JDK 8 后它被限制为内部使用,但 AtomicInteger.incrementAndGet() 等方法就是它的典型封装。
为什么 CAS 会失败:ABA 问题和循环开销真实存在
CAS 失败不只因为“别人改了”,更常见的是 ABA 问题:某变量从 A → B → A,CAS 发现仍是 A 就误认为没被修改过。这在引用类型中尤其危险——比如一个栈顶节点被弹出又压入同地址对象,AtomicReference 的 CAS 可能成功,但逻辑已错乱。
- 解决 ABA:用
AtomicStampedReference,它把版本号(stamp)和引用一起打包比较 - 循环重试成本:
getAndIncrement()在高争用下可能反复失败,CPU 白跑多个 cycle,比锁还耗资源 - 不能替代复合操作:CAS 只保证单变量原子,
i++和j++两个变量无法用一次 CAS 完成
Unsafe 是怎么被调用的:JDK 源码里藏着关键跳转
AtomicInteger.getAndIncrement() 最终走到 Unsafe.getAndAddInt(Object, long, int),而这个方法在 HotSpot VM 中是 native 实现。你可以从 OpenJDK 源码看到:Unsafe::compareAndSwapInt 对应 C++ 层的 Unsafe_CompareAndSwapInt,再调用平台相关汇编。
立即学习“Java免费学习笔记(深入)”;
注意两点:
- JDK 9+ 引入了
VarHandle,它是Unsafe的安全替代,但底层仍走相同 CPU 指令 - 不是所有 JVM 都支持 CAS:某些嵌入式或老版本 JVM 可能降级为锁实现(见
VM.supportsAtomicIntOperations()) - 字段偏移量(
valueOffset)由Unsafe.objectFieldOffset()计算,依赖 JVM 内存布局,不能硬编码
CAS 不是银弹:什么时候该避开它
过度依赖 CAS 容易掉进「伪无锁」陷阱。比如 ConcurrentLinkedQueue 虽用 CAS 更新 head/tail,但每次入队都要遍历到 tail,高并发下链表遍历本身就成了瓶颈;又比如 LongAdder 用分段 CAS + base 字段,本质是用空间换低争用,而不是单纯“不用锁”。
真正要警惕的是:
- 频繁 CAS 失败时,线程不会阻塞,但 CPU 使用率飙升,监控上表现为
os.cpuUsage高而吞吐没涨 - 调试困难:CAS 失败不抛异常,只能靠日志或断点看
compareAndSwapXXX返回值 - 与 GC 交互隐晦:对象引用的 CAS 成功,不代表该对象没被回收——尤其是弱引用或软引用场景
底层机制清晰不等于使用简单,CAS 的边界往往藏在争用模式、GC 周期和硬件缓存一致性协议(MESI)的细节里。










