cas通过cpu原子指令(如x86的cmpxchg)实现“比较并交换”,需指定内存位置v、预期值a和新值b,仅当v等于a时才原子更新为b,否则失败重试;其优势是无锁、低开销,但存在aba问题、自旋开销大及仅支持单变量原子性等局限。

CAS(Compare-And-Swap)是 Java 中实现无锁并发更新的核心机制,底层依赖 CPU 提供的原子指令(如 x86 的 cmpxchg),在 java.util.concurrent.atomic 包中被广泛使用。它不依赖传统锁(如 synchronized 或 ReentrantLock),而是通过“预期值比对 + 原子替换”来保证线程安全。
CAS 的基本原理和执行逻辑
CAS 操作包含三个参数:内存位置 V、预期原值 A、待更新的新值 B。其语义是:仅当 V 当前值等于 A 时,才将 V 更新为 B,并返回 true;否则不修改并返回 false。整个过程是原子的,由硬件指令直接保障,不会被线程调度打断。
例如:
- 一个
AtomicInteger counter = new AtomicInteger(0); - 线程1执行
counter.compareAndSet(0, 1)→ 成功,值变为 1; - 线程2同时执行
counter.compareAndSet(0, 1)→ 失败(此时值已是 1 ≠ 0),返回 false; - 线程2可选择重试(如用
incrementAndGet()内部的循环 CAS)。
Java 中 CAS 的典型使用方式
Java 不允许用户直接调用底层 CAS 指令,而是通过 Unsafe 类封装(JDK 9+ 受限制,但 VarHandle 和原子类仍间接使用)。开发者主要通过以下方式使用:
-
原子包装类:如
AtomicInteger、AtomicReference、AtomicStampedReference(解决 ABA 问题); -
显式调用 CAS 方法:如
atomicInt.compareAndSet(expected, newValue); -
内置 CAS 的复合操作:如
incrementAndGet()、getAndAccumulate()等,内部基于循环 CAS 实现; - ConcurrentHashMap / ThreadPoolExecutor 等并发容器/框架:在扩容、状态变更等关键路径中大量使用 CAS 控制竞争。
CAS 的局限性与应对策略
CAS 并非万能,需注意三类典型问题:
-
ABA 问题:V 从 A→B→A,CAS 误判为“未被修改”。可用
AtomicStampedReference加版本戳解决; - 循环时间开销:高竞争下反复失败重试,消耗 CPU。可通过退避(如 Thread.yield())、限制重试次数或改用锁降级缓解;
-
只能保证单变量原子性:无法直接对多个字段做原子更新。可用
AtomicReference<object></object>封装,或借助VarHandle的多字段访问控制(JDK 9+)。
无锁 ≠ 无竞争,理解 CAS 的适用边界
CAS 适合读多写少、临界区极小(如计数器、状态标志位)的场景。若更新逻辑复杂、涉及 I/O 或长时间计算,盲目用 CAS 循环反而降低吞吐。真实高性能并发结构(如 Disruptor、Netty 的 FastThreadLocal)往往混合使用 CAS、缓存行填充(@Contended)、分段思想等协同优化。
本质上,CAS 是把“阻塞等待”转为“主动验证+轻量重试”,性能优势建立在低冲突前提下。合理设计数据结构、控制共享粒度,比单纯套用 CAS 更重要。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











