CAS原理

深入理解 CAS 的核心原理,以及它在多线程场景下的工作机制、优缺点和实际应用,我会从基础概念到底层实现,用通俗易懂的方式帮你讲清楚。
 

CAS 核心原理

 
CAS 是 Compare-And-Swap(比较并交换) 的缩写,是一种无锁的原子操作机制,也是实现并发编程的核心思想之一。它的核心目标是:在不使用锁的情况下,保证多线程对共享变量操作的原子性。
 

1. CAS 的基本逻辑

 
CAS 操作涉及三个核心参数:
 
  • V:要修改的共享变量(内存地址)
  • A:预期值(线程读取到的变量当前值)
  • B:新值(线程想要把变量改成的值)
 
CAS 的执行流程可以概括为:
 
线程在更新变量 V 时,先对比 V 的当前值是否等于自己之前读取的预期值 A
 
  • 如果相等,说明没有其他线程修改过这个变量,就把 V 的值更新为新值 B,返回操作成功;
  • 如果不相等,说明其他线程已经修改过这个变量,当前线程不做更新,返回操作失败(线程可选择重试、放弃或执行其他逻辑)。
 
 
这个对比 + 交换的过程是CPU 级别的原子指令(如 x86 架构的 cmpxchg 指令),由硬件保证原子性,无需加锁。
 

2. 通俗举例(理解 CAS 逻辑)

 
假设两个线程(线程 1、线程 2)要对共享变量 j=0 执行加 1 操作,用 CAS 实现的过程:
 
  1. 线程 1 读取 j 的值为 0(预期值 A=0),准备更新为 1(新值 B=1);
  2. 线程 1 执行 CAS:对比内存中 j 的值是否等于 0 → 是,将 j 改为 1,操作成功;
  3. 线程 2 也读取 j 的值为 0(预期值 A=0),准备更新为 1(新值 B=1);
  4. 线程 2 执行 CAS:对比内存中 j 的值(此时已被线程 1 改为 1)≠ 预期值 0 → 操作失败;
  5. 线程 2 可选择自旋重试:重新读取 j 的值(现在是 1),再执行 CAS 把 j 改为 2
 

3. Java 中的 CAS 实现(以 AtomicInteger 为例)

 
Java 中 java.util.concurrent.atomic 包下的原子类(如 AtomicInteger)就是基于 CAS 实现的,核心依赖 Unsafe 类(提供底层内存操作)。
 
核心代码示例(简化版):
 
java
public class AtomicInteger extends Number implements java.io.Serializable {
    // 共享变量(volatile 保证可见性)
    private volatile int value;
    // Unsafe 类(JDK 内部类,提供底层 CAS 操作)
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset; // value 变量的内存地址偏移量

    static {
        try {
            // 获取 value 变量在内存中的偏移量
            valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    // 原子加1操作(核心 CAS 逻辑)
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

    // Unsafe 的 getAndAddInt 方法底层逻辑(简化)
    // var1:当前对象(AtomicInteger 实例)
    // var2:value 变量的内存偏移量
    // var4:要增加的值(如1)
    public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            // 1. 读取当前内存中的值(预期值 A)
            var5 = this.getIntVolatile(var1, var2);
            // 2. 执行 CAS:对比内存值是否等于 var5,是则更新为 var5+var4
            //    循环直到 CAS 成功(自旋)
        } while (!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
        return var5;
    }
}
关键说明:
  • valuevolatile 修饰,保证多线程间的可见性(一个线程修改后,其他线程能立即看到最新值);
  • compareAndSwapInt 是 native 方法,底层调用 CPU 原子指令,保证对比 + 交换的原子性;
  • 失败时通过 do-while 循环自旋重试,直到 CAS 成功。

4. CAS 的优缺点

优点
  • 无锁开销:无需加锁 / 解锁,避免了线程上下文切换、锁等待的性能损耗;
  • 原子性:由硬件指令保证,比锁更高效(尤其高并发低冲突场景);
  • 非阻塞:线程不会被挂起,失败后可重试或放弃,响应性更好。
 
缺点
  • ABA 问题:
     
    核心问题:变量值从 A 改为 B,又改回 A,CAS 会认为变量未被修改,导致逻辑错误。
     
    解决方案:使用版本号 / 时间戳(如 AtomicStampedReference),把 “值 + 版本号” 作为对比依据。
  • 自旋开销:
     
    高并发高冲突场景下,线程会一直自旋重试,占用 CPU 资源(可限制重试次数或改用锁)。
  • 只能保证单一变量的原子性:
     
    CAS 只能操作单个变量,如果是多变量复合操作(如 j = a + b),无法保证原子性,仍需用锁。
 

5. CAS vs 锁(核心区别)

 
特性CAS(无锁)synchronized/ReentrantLock(有锁)
核心机制 硬件原子指令 + 自旋 操作系统内核态锁(阻塞 / 唤醒线程)
性能 低冲突时极高,高冲突时下降 低冲突时略差,高冲突时更稳定
适用场景 单一变量原子操作 复合操作、多变量操作
问题 ABA、自旋开销 死锁、上下文切换开销
 

总结

 
  1. CAS 是硬件级别的原子操作,通过 “比较预期值 - 交换新值” 实现无锁原子更新,核心参数是内存地址 V、预期值 A、新值 B;
  2. Java 中 AtomicInteger 等原子类基于 CAS 实现,结合 volatile 保证可见性;
  3. CAS 优点是无锁高效,缺点是存在 ABA 问题、自旋开销,且仅适用于单一变量操作;
  4. 实际开发中,单一变量优先用 CAS(Atomic 类),复合操作优先用锁。
posted @ 2026-01-07 14:21  野鹤闲人  阅读(11)  评论(0)    收藏  举报