CAS与原子操作类Atomic

CAS与原子操作类Atomic

CAS(Compare And Swap)是并发编程的底层基石,Java 的原子类都基于 CAS 实现。本文从原理到应用全面解析。

CAS 原理

CAS 是一条 CPU 原子指令,包含三个操作数:

  • 内存位置 V
  • 预期值 A
  • 新值 B

当且仅当 V 的值等于 A 时,才将 V 的值更新为 B,否则什么都不做。

while (true) {
int current = get(); // 读取当前值
int newValue = current + 1; // 计算新值
if (compareAndSwap(current, newValue)) { // CAS操作
break; // 成功
}
// 失败,重试
}

Java 中的 CAS 实现

Unsafe 类

public final class Unsafe {
// 比较并交换对象字段
public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x);

// 比较并交换int
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);

// 比较并交换long
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);
}

注意Unsafe 不能直接调用,原子类内部使用。

Atomic 原子类家族

基本类型

AtomicInteger ai = new AtomicInteger(0);

// 原子递增
int newVal = ai.incrementAndGet(); // ++i
int oldVal = ai.getAndIncrement(); // i++

// 原子递减
ai.decrementAndGet();

// 原子加法
ai.addAndGet(5); // +5后返回
ai.getAndAdd(5); // 返回原值,再加5

// CAS操作
boolean success = ai.compareAndSet(0, 1); // 当前为0则设为1

数组类型

AtomicIntegerArray array = new AtomicIntegerArray(10);

array.set(0, 100);
int val = array.incrementAndGet(0); // 对索引0原子+1

引用类型

AtomicReference<User> ref = new AtomicReference<>();

// 原子更新引用
ref.set(new User("张三"));
User old = ref.getAndSet(new User("李四"));

// CAS更新
User expected = ref.get();
ref.compareAndSet(expected, new User("王五"));

字段更新器

public class User {
private volatile int age; // 必须用volatile
}

AtomicIntegerFieldUpdater<User> updater =
AtomicIntegerFieldUpdater.newUpdater(User.class, "age");

User user = new User();
updater.incrementAndGet(user); // 对user.age原子+1

累加器(JDK 8+)

高并发场景下比 AtomicLong 性能更好:

LongAdder adder = new LongAdder();
adder.increment();
adder.add(100);
long sum = adder.sum();

原理:内部维护 Cell 数组,线程分散到不同 Cell 上累加,最后求和。

LongAccumulator accumulator = new LongAccumulator(Long::max, 0);
accumulator.accumulate(100);
accumulator.accumulate(200);
long max = accumulator.get(); // 200

ABA 问题

什么是 ABA 问题

线程A读取值A
线程B将A改为B,又改回A
线程A的CAS操作成功,但实际上值已经被修改过
// 栈的ABA问题示例
AtomicReference<Node> top = new AtomicReference<>();

// 线程A:
Node node1 = top.get(); // 读取top=A
// ... 被中断 ...

// 线程B:
top.compareAndSet(A, B); // A->B
top.compareAndSet(B, A); // B->A(A被重新压入)

// 线程A恢复:
top.compareAndSet(A, newNode); // 成功!但栈结构已变化

解决方案:AtomicStampedReference

增加版本号(stamp)控制:

AtomicStampedReference<String> ref = 
new AtomicStampedReference<>("A", 0);

int[] stampHolder = new int[1];
String value = ref.get(stampHolder);
int stamp = stampHolder[0];

// 更新时需要匹配值和版本号
ref.compareAndSet("A", "B", stamp, stamp + 1);

AtomicMarkableReference

另一种方案,使用 boolean 标记:

AtomicMarkableReference<String> ref = 
new AtomicMarkableReference<>("A", false);

boolean[] markHolder = new boolean[1];
String value = ref.get(markHolder);

ref.compareAndSet("A", "B", false, true);

CAS 的优缺点

优点

  1. 无锁:没有线程阻塞和唤醒开销
  2. 高性能:竞争不激烈时效率极高
  3. 原子性:硬件级别保证

缺点

  1. 自旋开销:竞争激烈时 CPU 空转
  2. ABA 问题:需要额外处理
  3. 只能保证单个变量原子性:无法解决多变量一致性问题

性能对比

// 场景:10个线程各累加100000次

// synchronized
private long count1;
public synchronized void increment1() { count1++; }

// AtomicLong
private AtomicLong count2 = new AtomicLong(0);
public void increment2() { count2.incrementAndGet(); }

// LongAdder(推荐)
private LongAdder count3 = new LongAdder();
public void increment3() { count3.increment(); }
实现 低竞争 高竞争
synchronized
AtomicLong 差(大量自旋)
LongAdder 很好(分散竞争)

实际应用场景

1. 计数器

public class ConcurrentCounter {
private final LongAdder count = new LongAdder();

public void increment() {
count.increment();
}

public long get() {
return count.sum();
}
}

2. 序列号生成

public class IdGenerator {
private final AtomicLong sequence = new AtomicLong(0);

public long nextId() {
return sequence.incrementAndGet();
}
}

3. 乐观锁实现

public class OptimisticLock {
private final AtomicStampedReference<Data> ref;

public void update(Data newData) {
int[] stamp = new int[1];
Data current = ref.get(stamp);

// 处理数据...
Data processed = process(current, newData);

// CAS更新
if (!ref.compareAndSet(current, processed, stamp[0], stamp[0] + 1)) {
throw new ConcurrentModificationException();
}
}
}

总结

CAS 是无锁并发的基础,Java 通过 sun.misc.Unsafe 提供 CAS 能力,上层封装了丰富的原子类:

  • AtomicXxx:基本类型原子操作
  • AtomicXxxArray:数组原子操作
  • AtomicReference:引用原子操作
  • AtomicStampedReference:解决 ABA 问题
  • LongAdder/LongAccumulator:高并发累加器

在竞争激烈的高并发场景下,优先使用 LongAdder 替代 AtomicLong


   转载规则


《CAS与原子操作类Atomic》 小乐 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录