"为什么AtomicInteger在高并发下性能暴跌?CAS操作究竟如何实现线程安全?LongAdder凭什么比AtomicLong快10倍?"
本文通过分布式全局ID生成器与实时库存扣减系统两大场景,结合Hotspot源码与汇编指令解析,彻底讲透CAS机制的实现原理与工程实践。
一、CAS核心原理剖析
1. 硬件级原子操作支持
; X86 LOCK CMPXCHG指令实现
LOCK CMPXCHG [mem], reg
; 保证比较交换操作的原子性
CAS操作三要素:
-
内存地址(V)
-
预期原值(A)
-
新值(B)
执行流程:
sequenceDiagram
participant T1 as 线程1
participant T2 as 线程2
participant Mem as 主内存
T1->>Mem: 读取V=10
T2->>Mem: 读取V=10
T1->>Mem: CAS(V=10→15)
Mem-->>T1: 成功
T2->>Mem: CAS(V=10→20)
Mem-->>T2: 失败(值已变)
2. AtomicInteger源码解析
public class AtomicInteger {
private volatile int value;
public final int incrementAndGet() {
return U.getAndAddInt(this, VALUE, 1) + 1;
}
// Unsafe类CAS操作
public final boolean compareAndSet(int expect, int update) {
return U.compareAndSetInt(this, VALUE, expect, update);
}
}
实现关键:
-
volatile保证可见性
-
Unsafe类调用本地CAS方法
-
自旋重试策略(非阻塞)
二、高并发场景性能优化
1. LongAdder分段锁设计
public class LongAdder {
final Cell[] cells; // 分段计数单元
@sun.misc.Contended static final class Cell {
volatile long value;
}
public void add(long x) {
Cell[] cs; long b, v; int m; Cell c;
if ((cs = cells) != null || !casBase(b = base, b + x)) {
// 存在竞争时使用分段计数
int index = getProbe();
if (cs == null || (m = cs.length - 1) < 0 ||
(c = cs[index & m]) == null || !(uncontended = c.cas(v = c.value, v + x)))
longAccumulate(x, null, uncontended, index);
}
}
}
性能优势:
-
分散热点:通过Cell数组减少CAS竞争
-
@Contended注解:避免伪共享(缓存行填充)
-
惰性初始化:无竞争时使用base变量
2. ABA问题与解决方案
场景模拟:
AtomicReference<String> ref = new AtomicReference<>("A");
// 线程1:A→B→A
ref.compareAndSet("A", "B");
ref.compareAndSet("B", "A");
// 线程2:CAS判断仍为A,认为未被修改过
ref.compareAndSet("A", "C"); // 成功!
解决方案:
AtomicStampedReference<String> stampedRef =
new AtomicStampedReference<>("A", 0);
// 线程1修改时更新版本号
stampedRef.compareAndSet("A", "B", 0, 1);
stampedRef.compareAndSet("B", "A", 1, 2);
// 线程2检查版本号
stampedRef.compareAndSet("A", "C", 0, 1); // 失败,版本号已变
三、无锁编程实战案例
1. 无锁栈实现
public class LockFreeStack<T> {
private AtomicReference<Node<T>> top = new AtomicReference<>();
public void push(T item) {
Node<T> newHead = new Node<>(item);
Node<T> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
public T pop() {
Node<T> oldHead;
Node<T> newHead;
do {
oldHead = top.get();
if (oldHead == null) return null;
newHead = oldHead.next;
} while (!top.compareAndSet(oldHead, newHead));
return oldHead.item;
}
}
2. 高性能计数器(JMH压测数据)
实现方案 | 线程数 | QPS(万次/秒) |
---|---|---|
synchronized | 32 | 12.3 |
AtomicLong | 32 | 89.7 |
LongAdder | 32 | 356.4 |
四、常见问题QA
-
Q:CAS操作会导致CPU飙升吗?
☆会!自旋时间过长时应配合退避策略(如指数退避) -
Q:AtomicReference和AtomicStampedReference区别?
☆后者通过版本号解决ABA问题 -
Q:如何检测伪共享问题?
☆使用perf c2c
工具分析缓存行竞争