一句话总结

CAS(Compare-And-Swap)是Java中基于硬件的无锁并发机制,通过比较内存值与预期值进行原子更新。核心方法包含内存地址、预期值和新值,若匹配则更新,否则重试或放弃。常用于AtomicInteger等原子类实现线程安全操作,避免加锁开销。存在ABA问题(可通过版本号标记解决),适用于低竞争场景。

详细解析

在 Java 中,CAS(Compare-And-Swap,比较并交换) 是一种无锁的原子操作技术,用于在多线程环境下实现变量的线程安全修改。它通过 CPU 底层指令保证操作的原子性,避免了传统锁(如synchronized)带来的线程阻塞和上下文切换开销,是 Java 并发编程(JUC 包)的核心技术之一。

一、CAS 的核心思想

CAS 的操作逻辑可以概括为三个步骤:
比较(Compare)交换(Swap)
具体来说,CAS 包含三个关键参数:

  • V(Value):变量在内存中的实际值(目标内存地址的值)。
  • A(Expected):线程认为变量当前应有的“预期值”。
  • B(NewValue):线程希望将变量更新为的“新值”。

操作逻辑
当且仅当内存中的实际值 V
等于 预期值 A 时,才将 V 更新为 B;否则不执行任何操作(保持 V 不变)。整个过程由 CPU 指令保证原子性,不可被中断。

二、Java 中的 CAS 实现

Java 中 CAS 的底层实现依赖于sun.misc.Unsafe类(JDK 内部使用,不提议直接调用)。Unsafe提供了一系列compareAndSwapXxx方法(如compareAndSwapInt、compareAndSwapObject),这些方法直接调用 CPU 的原子指令(如 x86 的cmpxchg指令),确保操作的原子性。

示例:AtomicInteger的原子递增

Java 的原子类(如AtomicInteger)是 CAS 的典型应用。以incrementAndGet()方法为例,其内部通过循环 CAS 实现原子递增:

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset; // value 字段的内存偏移量

    static {
        try {
            valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value; // 保证可见性

    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
}

unsafe.getAndAddInt方法的底层逻辑是一个 自旋循环

public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset); // 读取当前值(volatile 保证可见性)
        // 循环 CAS:如果当前值等于预期值 v,则更新为 v + delta
    } while (!compareAndSwapInt(o, offset, v, v + delta)); 
    return v;
}
  • 自旋:如果 CAS 失败(说明其他线程修改了值),则重新读取当前值并再次尝试 CAS,直到成功。
  • volatile 变量:value被声明为volatile,保证多线程间的可见性(任何线程对value的修改会立即被其他线程感知)。

三、CAS 的应用场景

CAS 在 Java 并发编程中被广泛使用,典型场景包括:

  1. 原子类(AtomicXXX):如AtomicInteger、AtomicBoolean,用于实现无锁的计数器、状态标记等。
  2. 并发容器(JUC 包):如ConcurrentHashMap(JDK 8+)的节点更新、ConcurrentLinkedQueue的链表操作,通过 CAS 替代锁提升性能。
  3. 锁的底层实现:如ReentrantLock的tryLock()方法通过 CAS 尝试获取锁,减少线程阻塞概率。

四、CAS 与锁的对比

特性

CAS(无锁)

锁(如 synchronized)

线程状态

自旋(不阻塞)

阻塞(需唤醒)

适用场景

低竞争、短时间操作

高竞争、长时间操作

上下文切换

无(避免线程阻塞)

可能频繁(阻塞/唤醒开销大)

复杂度

需处理 ABA、自旋开销等问题

简单(JVM 优化后性能提升)

© 版权声明

相关文章

1 条评论

您必须登录才能参与评论!
立即登录