CAS:Compare-And-Swap
什么是 CAS?
CAS(Compare-And-Swap,比较并交换)是一种无锁的原子操作,
由 CPU 硬件直接支持(x86 的 CMPXCHG 指令)。
其语义为:
CAS 语义
原子地执行:if (内存地址V的当前值 == 期望值A) { 将V的值更新为新值B; return true; } else { return false; }
整个比较+更新是一个不可分割的原子操作,无需加锁。
CAS 操作流程:
线程 A 执行 CAS(V, expected=5, newValue=6):
┌─────────────────────────────────────────────┐
│ 1. 读取内存 V 的当前值 │
│ 2. 比较:当前值 == expected(5)? │
│ YES → 将 V 更新为 newValue(6),返回 true │
│ NO → 不更新,返回 false │
│ 注意:步骤 1+2+3 是 CPU 级别的原子操作 │
└─────────────────────────────────────────────┘
若 CAS 失败(被其他线程抢先修改):
┌────────────────────────────────────────────┐
│ 重新读取最新值 → 重新计算 → 再次尝试 CAS │
│ (自旋重试,直到成功) │
└────────────────────────────────────────────┘
CAS vs 锁
CAS(乐观锁)
- 假设没有冲突,直接操作
- 冲突时重试(自旋)
- 无内核态切换
- 高竞争时自旋浪费 CPU
- 适合:低竞争、操作短小
synchronized/Lock(悲观锁)
- 假设有冲突,先加锁
- 冲突时阻塞(休眠)
- 涉及内核态切换
- 高竞争时线程排队等待
- 适合:高竞争、操作复杂
Atomic 原子类
AtomicInteger / AtomicLong
import java.util.concurrent.atomic.*;
AtomicInteger counter = new AtomicInteger(0);
// 基本操作
counter.get(); // 读取当前值(volatile 语义)
counter.set(10); // 设置值(volatile 写)
counter.getAndIncrement(); // i++(返回旧值)
counter.incrementAndGet(); // ++i(返回新值)
counter.getAndAdd(5); // i += 5(返回旧值)
counter.addAndGet(5); // i += 5(返回新值)
counter.compareAndSet(10, 20); // CAS:若当前为10,则改为20
// Java 9+ 新增
counter.getAndUpdate(v -> v * 2); // 原子更新,返回旧值
counter.updateAndGet(v -> v * 2); // 原子更新,返回新值
counter.accumulateAndGet(3, Integer::sum); // 原子 reduce
// 实现线程安全计数器
public class SafeCounter {
private final AtomicInteger count = new AtomicInteger();
public void increment() { count.incrementAndGet(); }
public int get() { return count.get(); }
}
AtomicReference:原子引用
// AtomicReference<T> 对对象引用进行原子 CAS 操作
AtomicReference<String> atomicRef = new AtomicReference<>("initial");
// CAS:若当前值为 "initial",则替换为 "updated"
boolean success = atomicRef.compareAndSet("initial", "updated");
System.out.println(success); // true
// 无锁栈(Treiber Stack)实现
public class LockFreeStack<T> {
private static class Node<T> {
final T value;
Node<T> next;
Node(T value) { this.value = value; }
}
private final AtomicReference<Node<T>> top = new AtomicReference<>();
public void push(T value) {
Node<T> newNode = new Node<>(value);
do {
newNode.next = top.get(); // 读取当前栈顶
} while (!top.compareAndSet(newNode.next, newNode)); // CAS 设置新栈顶
}
public T pop() {
Node<T> current;
do {
current = top.get();
if (current == null) return null;
} while (!top.compareAndSet(current, current.next)); // CAS 弹出栈顶
return current.value;
}
}
ABA 问题
CAS 只比较值是否相等,但值相同不代表没有变化。假设变量初始为 A, 线程 1 准备做 CAS(A→C),此时线程 2 先把 A 改为 B,再改回 A——线程 1 的 CAS 依然成功, 但实际上数据已经被修改过了。这就是 ABA 问题。
// 解决方案:AtomicStampedReference(版本戳)
AtomicStampedReference<String> ref =
new AtomicStampedReference<>("A", 0); // 初始值,初始戳=0
int[] stamp = new int[1];
String current = ref.get(stamp); // 同时获取值和戳
// CAS:同时比较值和戳,两者都匹配才更新
boolean ok = ref.compareAndSet(
"A", "C", // 期望值→新值
stamp[0], stamp[0] + 1 // 期望戳→新戳(每次操作递增)
);
// 即使值从 A→B→A,但戳是唯一递增的,不会误判
// AtomicMarkableReference:用 boolean 标记(简化版)
AtomicMarkableReference<Node> nodeRef =
new AtomicMarkableReference<>(node, false);
nodeRef.compareAndSet(node, newNode, false, true); // 同时更新值和标记
LongAdder:高性能累加器
AtomicLong 在高并发下(大量线程同时 CAS)会产生大量竞争,导致频繁自旋重试,性能下降。
LongAdder(Java 8 引入)通过分散热点解决这一问题:
内部维护一个 base 变量和多个 Cell(每个 Cell 是一个 long),
每个线程优先更新自己的 Cell,读取时汇总所有 Cell + base。
AtomicLong:所有线程竞争同一个变量
Thread-1 ──► CAS(counter) ← 竞争!
Thread-2 ──► CAS(counter) ← 重试!
Thread-3 ──► CAS(counter) ← 自旋!
Thread-4 ──► CAS(counter)
LongAdder:线程分散到不同 Cell,减少竞争
Thread-1 ──► Cell[0] += 1 ← 互不干扰
Thread-2 ──► Cell[1] += 1
Thread-3 ──► Cell[2] += 1
Thread-4 ──► Cell[3] += 1
sum() = base + Cell[0] + Cell[1] + Cell[2] + Cell[3]
LongAdder adder = new LongAdder();
adder.increment(); // +1
adder.decrement(); // -1
adder.add(10); // += 10
long total = adder.sum(); // 获取总和(近似值,并发时可能略有偏差)
long exact = adder.sumThenReset(); // 获取并重置(常用于统计)
// 性能对比:高并发计数器
// AtomicLong:竞争激烈时 CAS 不断失败重试,QPS 约 50M/s(8核)
// LongAdder:分散写,QPS 约 400M/s(8核),性能提升8倍+
// 同类:LongAccumulator(支持自定义累积函数)
LongAccumulator maxVal = new LongAccumulator(Long::max, Long.MIN_VALUE);
maxVal.accumulate(42); // 原子 max 操作
maxVal.accumulate(100);
System.out.println(maxVal.get()); // 100
Atomic 数组类
// AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
AtomicIntegerArray arr = new AtomicIntegerArray(10); // 长度10的原子整型数组
arr.set(0, 100); // 设置 arr[0] = 100
arr.getAndIncrement(0); // arr[0]++
arr.compareAndSet(0, 101, 200); // CAS arr[0]: 101 → 200
// 使用场景:并行更新数组的不同位置(无需整体加锁)
AtomicIntegerArray histogram = new AtomicIntegerArray(256);
// 多线程并行统计字节频率
for (byte b : data) {
histogram.incrementAndGet(b & 0xFF);
}
Atomic 字段更新器
// 对已有类的 volatile 字段进行原子操作,无需修改原类
public class Node {
volatile int value; // 必须是 volatile!
}
// 创建更新器(一次性对象,可复用)
AtomicIntegerFieldUpdater<Node> updater =
AtomicIntegerFieldUpdater.newUpdater(Node.class, "value");
Node node = new Node();
updater.set(node, 10);
updater.compareAndSet(node, 10, 20);
System.out.println(node.value); // 20
VarHandle(Java 9+)
VarHandle(JEP 193,Java 9)是更底层、更通用的变量访问句柄,
可以对变量(字段、数组元素、静态变量)执行不同内存语义的读写操作,
替代了 Unsafe 和字段更新器的大部分使用场景。
getVolatile / setVolatile
带 volatile 语义的读写,等效于对 volatile 变量的读写,提供完整的内存可见性和有序性保证。
getOpaque / setOpaque
不提供跨线程可见性保证,但保证单线程内的顺序性。比 plain 语义强,比 volatile 弱。适用于线程本地或只需要单线程有序的场景。
getAcquire / setRelease
Acquire/Release 语义(C++ memory_order_acquire/release)。Acquire 读防止后续读写重排到前面;Release 写防止前面读写重排到后面。比 volatile 稍弱,性能更好。
compareAndSet / compareAndExchange
CAS 操作,compareAndSet 返回 boolean,compareAndExchange 返回实际观测到的旧值(失败时)。
import java.lang.invoke.*;
class Counter {
private volatile int count = 0;
// 获取 count 字段的 VarHandle
private static final VarHandle COUNT;
static {
try {
COUNT = MethodHandles.lookup()
.findVarHandle(Counter.class, "count", int.class);
} catch (Exception e) { throw new ExceptionInInitializerError(e); }
}
public int incrementAndGet() {
int v;
do {
v = (int) COUNT.getVolatile(this);
} while (!COUNT.compareAndSet(this, v, v + 1));
return v + 1;
}
// 使用 getAndAdd(VarHandle 内置的原子操作)
public int getAndAdd(int delta) {
return (int) COUNT.getAndAdd(this, delta);
}
}
原子类选型指南
| 场景 | 推荐 | 说明 |
|---|---|---|
| 单个 int/long 的线程安全操作 | AtomicInteger / AtomicLong | 通用,API 丰富 |
| 高并发累加计数器(只写,定期读) | LongAdder | 写分散,高并发性能远优于 AtomicLong |
| 高并发求最大/最小值 | LongAccumulator | 支持自定义累积函数 |
| 原子替换对象引用 | AtomicReference | 无锁状态切换 |
| 需要版本号防 ABA | AtomicStampedReference | 值+戳双重 CAS |
| 已有类的 volatile 字段原子操作 | VarHandle(Java 9+) | 取代字段更新器和 Unsafe |
| 数组元素的原子操作 | AtomicIntegerArray 等 | 每个元素独立原子操作 |
本章小结
- CAS(Compare-And-Swap)是无锁并发的基础,CPU 硬件原生支持原子的「比较+更新」操作。
- AtomicInteger/Long/Reference 基于 CAS 提供原子操作,适合低竞争场景。
- ABA 问题:值相同不代表没有修改,用
AtomicStampedReference添加版本戳解决。 - LongAdder 通过分散热点(多个 Cell)解决高并发下 AtomicLong 的自旋竞争,吞吐量提升数倍。
- VarHandle(Java 9+)提供细粒度的内存语义(plain/opaque/acquire-release/volatile),是操作变量的最底层安全 API。