在 Java 并发编程中,synchronized 虽然简单易用,但在高并发场景下,线程阻塞与唤醒带来的上下文切换开销会显著影响性能。为此,JUC 包提供了一套基于 CAS(Compare And Swap) 的无锁并发方案。CAS 是整个 JUC 体系的基石,AQS、原子类、锁实现等底层都依赖它。
本文将从原理、执行流程、底层实现、优缺点、典型缺陷及解决方案、实际应用等方面,全面深入解析 CAS。
一、CAS 是什么
CAS,即 Compare And Swap(比较并交换) ,是一种CPU 原语级别的原子操作。
它的核心思想是:在更新数据之前,先比较内存中的当前值是否等于预期值。如果相等,则认为该值未被其他线程修改,可以安全更新;否则不更新,直接返回失败。
整个比较与交换过程是原子不可分割的,不会被其他线程打断,因此可以在不加锁的情况下保证并发安全。
二、CAS 执行流程
CAS 操作包含三个核心操作数:
- V :内存地址中存储的实际值
- A :线程读取到的预期旧值
- B :需要写入的新值
执行步骤:
- 从内存中读取当前值 V;
- 判断 V 是否等于预期值 A;
- 如果相等,将 V 更新为新值 B,操作成功;
- 如果不相等,说明值已被其他线程修改,本次更新失败,可选择重试或放弃。
因为整个过程是原子的,所以即使多个线程同时执行 CAS,也不会出现数据竞争问题。
三、CAS 底层实现原理
CAS 并不是 Java 语言实现的逻辑,而是直接依赖硬件 CPU 指令。
-
Java 层 API Java 中通过
sun.misc.Unsafe类提供 CAS 操作,例如:compareAndSwapInt(Object o, long offset, int expected, int update)该方法是 native 方法,由 JVM 直接调用底层指令。
-
CPU 指令支持
- x86 架构:使用
LOCK CMPXCHG指令 - ARM 架构:使用
LDREX/STREX指令这些指令属于CPU 原子指令,硬件级别保证不可中断。
- x86 架构:使用
-
**为什么能保证原子性?**因为 CAS 是一条 CPU 指令,而不是多条指令组合,因此在执行过程中不会被线程调度打断,从而天然具备原子性。
四、CAS 的优点
1. 无锁、非阻塞
CAS 不使用锁,不会导致线程阻塞、挂起、唤醒,避免了大量线程上下文切换开销。
2. 高并发下性能优异
在读多写少、竞争不激烈的场景,CAS 性能远超 synchronized。
3. 轻量级实现
不依赖 JVM 锁升级机制(偏向锁→轻量级锁→重量级锁),直接操作内存,资源消耗极低。
4. 粒度极细
CAS 可以只针对一个变量进行原子操作,锁粒度远小于传统锁机制。
五、CAS 的三大缺陷(面试高频)
CAS 虽强,但并非完美,存在三个典型问题。
缺陷 1:ABA 问题
**现象:**线程 1 读取值为 A,准备 CAS 改为 B;线程 2 将 A → B → A;线程 1 执行 CAS,发现内存值仍然是 A,误认为没有变化,更新成功。
**问题本质:**CAS 只关心 "值是否相同",不关心 "值是否被改动过"。某些业务场景下,这种 "被改回来" 的情况会导致逻辑错误。
解决方案: 使用 版本号机制,每次修改都让版本号自增。Java 提供:
AtomicStampedReferenceAtomicMarkableReference
可以同时比较 "引用 + 版本号",彻底避免 ABA。
缺陷 2:自旋消耗 CPU
CAS 失败时,通常不会阻塞线程,而是通过循环不断重试,即自旋。
在高并发、竞争激烈的场景下:
- 大量线程同时自旋
- CPU 占用率飙升
- 系统性能急剧下降
解决方案:
- 限制自旋次数,超过次数则进入阻塞;
- 使用自适应自旋,根据历史竞争情况动态调整自旋次数;
- AQS 中就是自旋一定次数后,将线程放入同步队列并阻塞。
缺陷 3:只能保证单个变量的原子性
CAS 只能对一个变量 / 字段执行原子操作。如果需要同时更新多个变量,并保证整体原子性,CAS 无法实现。
解决方案:
- 将多个变量封装成一个对象,使用
AtomicReference; - 业务允许的情况下,使用锁机制保证原子性。
六、CAS 在 Java 中的核心应用
1. JUC 原子类
AtomicInteger、AtomicLong、AtomicBoolean、AtomicReference 等,底层完全基于 CAS 实现。
2. AQS 同步器
AQS 是 ReentrantLock、Semaphore、CountDownLatch 等工具的底层框架。CAS 在 AQS 中主要用于:
- 修改
state同步状态 - 节点入队
- 设置等待状态
- 唤醒标记更新
3. 无锁队列与无锁缓存
高性能框架(如 Netty、Disruptor)大量使用 CAS 实现无锁队列,提升吞吐。
4. 高并发计数器、限流组件
秒杀、限流、统计访问量等场景,普遍使用原子类 + CAS。
七、CAS 与 synchronized 对比
| 特性 | CAS | synchronized |
|---|---|---|
| 实现方式 | CPU 原子指令 | JVM 内置监视器锁 |
| 锁机制 | 无锁、非阻塞 | 阻塞式悲观锁 |
| 原子范围 | 单个变量 | 代码块 / 方法 |
| 并发冲突 | 自旋重试 | 线程阻塞 |
| 性能 | 低竞争时极高 | 高竞争下更稳定 |
| 适用场景 | 读多写少、高并发 | 写多读少、逻辑复杂 |
简单总结:竞争小用 CAS,竞争大用 synchronized。
八、总结
- CAS 是 Compare And Swap,一种 CPU 级原子操作,是 Java 无锁并发的基础。
- 核心流程:比较内存值与预期值,相同则更新,不同则失败。
- 优点:无锁、高性能、轻量、非阻塞。
- 缺陷:ABA 问题、CPU 自旋消耗、仅支持单个变量原子性。
- 解决方案:版本号、限制自旋、对象封装。
- CAS 支撑了整个 JUC 体系,是中高级 Java 工程师必须掌握的核心知识点。