一句话总结
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 并发编程中被广泛使用,典型场景包括:
- 原子类(AtomicXXX):如AtomicInteger、AtomicBoolean,用于实现无锁的计数器、状态标记等。
- 并发容器(JUC 包):如ConcurrentHashMap(JDK 8+)的节点更新、ConcurrentLinkedQueue的链表操作,通过 CAS 替代锁提升性能。
- 锁的底层实现:如ReentrantLock的tryLock()方法通过 CAS 尝试获取锁,减少线程阻塞概率。
四、CAS 与锁的对比
|
特性 |
CAS(无锁) |
锁(如 synchronized) |
|
线程状态 |
自旋(不阻塞) |
阻塞(需唤醒) |
|
适用场景 |
低竞争、短时间操作 |
高竞争、长时间操作 |
|
上下文切换 |
无(避免线程阻塞) |
可能频繁(阻塞/唤醒开销大) |
|
复杂度 |
需处理 ABA、自旋开销等问题 |
简单(JVM 优化后性能提升) |



谢谢老板鼓励