一文彻底掌握 CAS(Compare-And-Swap)

内容纲要

一文彻底掌握 CAS(Compare-And-Swap)
—— 并发编程绕不开的底层真相

本文是一篇“一次看懂、以后不再查资料”的 CAS 终极文章

目标很明确:只要你把这篇文章完整看完,以后无论是面试、写并发代码、看源码、做系统设计,再遇到 CAS,都不需要再查任何资料。


先给结论(重要)

CAS 是一种由 CPU 硬件直接支持的原子操作,用“比较 + 交换”的方式,在不加锁的情况下完成并发安全更新,是现代高并发系统的底层基石。

你后面看到的:

  • AtomicInteger
  • ConcurrentHashMap
  • AQS
  • ReentrantLock
  • LongAdder
  • 无锁队列
  • 高性能并发框架

底层都绕不开 CAS。


一、为什么会有 CAS?

1. 并发的根本问题是什么?

多个线程同时修改同一份共享数据

比如:

线程 A:value = 1 → +1 → 写回
线程 B:value = 1 → +1 → 写回

结果本该是 2,却变成了 1

2. 传统解决方案:加锁

synchronized (lock) {
    value++;
}

锁的问题不是不能用,而是代价很高:

  • 阻塞线程
  • 上下文切换
  • 内核态参与
  • 高并发下吞吐下降明显

于是出现一个问题:

能不能不加锁,也保证并发安全?

答案就是:CAS


二、CAS 到底是什么?

1. CAS 的完整定义

CAS = Compare-And-Swap(比较并交换)

它是一个原子操作,由 CPU 指令直接保证。

标准形式:

CAS(内存地址 V, 期望值 A, 新值 B)

执行规则只有一条:

  • 如果 V == A → 把 V 改成 B
  • 如果 V != A → 什么也不做

整个过程不可被中断。


2. CAS 的一句话模型

“如果现在的值还是我看到的那个值,我才更新;否则我放弃。”


三、CAS 是怎么实现并发安全的?

1. 核心思想:乐观并发控制

CAS 的思路不是“先锁住”,而是:

  • 假设没人改
  • 我试着改
  • 如果发现被别人抢先改了
  • 那我再试一次

这就是乐观锁思想


2. CAS 的典型使用方式:自旋

while (true) {
    int old = value;
    int next = old + 1;
    if (CAS(value, old, next)) {
        break;
    }
}

这个过程叫:

CAS 自旋(Spin + Retry)

失败不是异常,是预期内行为


四、CAS 为什么“无锁”但又是安全的?

1. 关键在“原子性”

CAS 的原子性不是 JVM 保证的,而是:

CPU 指令级别保证的

例如:

  • x86:CMPXCHG
  • ARM:LDREX / STREX

在硬件层面:

  • 比较
  • 判断
  • 写入

是一个不可分割的整体


2. CAS vs 锁的本质区别

对比维度 CAS
是否阻塞
是否切换线程
冲突处理方式 重试 等待
适合场景 低/中竞争 高竞争
性能 稳定但慢

五、CAS 的三大经典问题(面试必问)

1️⃣ ABA 问题(必须理解)

什么是 ABA?

变量变化过程:

A → B → A

CAS 只关心:

现在是不是 A?

不知道中间发生过变化

为什么这是问题?

在以下场景会出错:

  • 链表
  • 对象引用
  • 资源回收

解决方案

引入版本号(或时间戳)

(A, version=1) → (B, 2) → (A, 3)

Java 对应工具:

  • AtomicStampedReference
  • AtomicMarkableReference

2️⃣ 自旋导致 CPU 空转

CAS 失败时会:

  • 不阻塞
  • 不休眠
  • 一直重试

高竞争下:

  • CPU 使用率飙升
  • 吞吐反而下降

结论:

CAS 不是银弹,高并发 ≠ 一定用 CAS。


3️⃣ 只能保证“一个变量”的原子性

CAS 天生只能操作:

  • 一个内存位置

无法直接保证:

  • 多变量一致性
  • 复杂事务逻辑

这也是为什么:

CAS 通常是并发工具的“底层原语”,而不是直接业务工具。


六、CAS 在 Java 并发体系中的真实位置

1. Atomic 类族

  • AtomicInteger
  • AtomicLong
  • AtomicBoolean
  • AtomicReference

👉 最纯粹的 CAS 封装


2. AQS(核心中的核心)

  • ReentrantLock
  • Semaphore
  • CountDownLatch

AQS 内部关键状态:

volatile int state;

所有状态变更:

全部靠 CAS


3. ConcurrentHashMap

  • 桶初始化
  • 节点插入
  • 扩容协作

大量使用 CAS + synchronized 混合策略。


4. LongAdder

为了解决:

  • AtomicLong 在高并发下 CAS 失败率高

方案是:

CAS + 分段 + 低冲突设计


七、什么时候该用 CAS?

适合用 CAS 的场景

  • 计数器
  • 状态标志
  • 配置热更新
  • 并发统计
  • 无锁队列 / 栈
  • 框架级并发组件

不适合直接用 CAS 的场景

  • 高冲突写操作
  • 多变量强一致
  • 复杂业务事务
  • 可读性要求高的业务代码

八、一句“工程级”总结(非常重要)

CAS 是并发世界的“原子操作基石”,不是业务代码的通用解决方案。

  • 一定要理解它
  • 不一定要自己手写它
  • 大多数时候,用成熟并发工具类

九、终极记忆模型(背下来就够了)

CAS = 硬件原子指令 + 乐观并发思想 + 自旋重试机制

以及一句更狠的:

现代 Java 并发框架,本质上是在“如何更聪明地使用 CAS”。


十、如果你再看到 CAS,可以直接这样理解

  • 看到 Atomic → CAS
  • 看到 AQS → CAS
  • 看到无锁 → CAS
  • 看到高性能并发 → CAS

你不需要再查资料,只需要问自己一句:

这里的并发冲突,是低频可重试,还是高频需要阻塞?

这就是 CAS 的全部价值判断。

close
arrow_upward