Atomic* / Striped64 / LongAdder 原子类源码精读
概述
java.util.concurrent.atomic 包提供无锁原子类:单值型(AtomicInteger / AtomicLong / AtomicReference)、数组型、字段更新器型(AtomicIntegerFieldUpdater),以及高并发计数利器 LongAdder(基于 Striped64 的 Cell 分散 + 汇总设计)。本文基于 OpenJDK 21 源码拆解其 CAS 循环与降竞争机制。
一、AtomicInteger 的 Unsafe 实现
1.1 结构与偏移量
// java.util.concurrent.atomic.AtomicInteger
public class AtomicInteger extends Number implements Serializable {
private static final long valueOffset;
static {
try {
// 反射获取 value 字段的偏移量(实例字段偏移)
valueOffset = Unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value; // 存储字段:volatile 保证可见性
}1.2 getAndIncrement() 的 CAS 循环
public final int getAndIncrement() {
return U.getAndAddInt(this, valueOffset, 1);
}
// jdk.internal.misc.Unsafe.getAndAddInt
@IntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset); // 读当前值
} while (!compareAndSetInt(o, offset, v, v + delta)); // CAS 直到成功
return v;
}getAndIncrement 的经典模式是 CAS 自旋:读旧值 → CAS 更新 → 失败则重读重试。低竞争下一次成功,高竞争下多个线程循环重试,但没有线程阻塞/挂起,相比 synchronized 省去上下文切换。
循环展开(等价于):
while (true) {
int current = get(); // volatile 读
if (compareAndSet(current, current + 1)) // CAS:成功则返回旧值
return current;
}1.3 lazySet(int) 的 putOrderedInt
public final void lazySet(int newValue) {
U.putOrderedInt(this, valueOffset, newValue); // 懒写:store-store 屏障
}lazySet 不保证立即可见(可能被其他线程晚一点读到),但避免了 volatile 写的全屏障成本,适合写多读少、读者容忍延迟的场景(如 AtomicBoolean 标记位、队列的终止标志)。
二、AtomicReference 与字段更新器
2.1 AtomicReference.compareAndSet
public final boolean compareAndSet(V expectedValue, V newValue) {
return VALUE.compareAndSet(this, expectedValue, newValue); // VarHandle 实现
}JDK 9+ 的 AtomicReference 内部用 VarHandle(private static final VarHandle VALUE)完成 CAS,语义同 Unsafe.compareAndSwapObject:期望值与当前引用相等才更新。
2.2 AtomicIntegerFieldUpdater 的反射 CAS
更新器用于不修改已有类(不能改成 AtomicInteger 字段)的场景,直接对目标类的普通 volatile int 字段做 CAS:
// java.util.concurrent.atomic.AtomicIntegerFieldUpdater
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>(tclass, fieldName, CallerSensitive.getReflectionCaller());
}
// 内部实现类
AtomicIntegerFieldUpdaterImpl(Class<T> tclass, String fieldName, Class<?> caller) {
final Field field = tclass.getDeclaredField(fieldName); // 反射取字段
final int mods = field.getModifiers();
// 校验:必须 volatile、非 static
if (!Modifier.isVolatile(mods)) throw new IllegalArgumentException("Must be volatile type");
this.offset = U.objectFieldOffset(field); // 获取偏移量
}// 使用示例(不改类定义,直接升级字段)
class Account {
volatile int balance;
}
AtomicIntegerFieldUpdater<Account> updater =
AtomicIntegerFieldUpdater.newUpdater(Account.class, "balance");
updater.addAndGet(account, 10); // 对普通字段做原子操作限制:目标字段必须
volatile、不能是static、不能用volatile子类字段覆盖父类字段等。
三、LongAdder 与 Striped64
3.1 设计动机
AtomicLong 在高并发下所有线程争抢同一个缓存行,CAS 大量失败重试。LongAdder 的思路:把计数分散到多个 Cell(缓存行隔离)上,各线程只更新自己命中的 Cell,最后 sum() 汇总——以空间换竞争。
// java.util.concurrent.atomic.Striped64(LongAdder 的父类)
abstract class Striped64 extends Number {
static final int NCPU = Runtime.getRuntime().availableProcessors(); // CPU 核数
transient volatile Cell[] cells; // Cell 数组(分散计数单元)
transient volatile long base; // 基础值(竞争低时直接用)
transient volatile int cellsBusy; // Cell 数组扩容/初始化的自旋锁标记
}3.2 Cell 与 @Contended 缓存行填充
@jdk.internal.vm.annotation.Contended // 关键注解:缓存行填充
static final class Cell {
volatile long value;
Cell(long x) { value = x; }
final boolean cas(long cmp, long val) {
return VALUE.compareAndSet(this, cmp, val);
}
}@Contended(Java 9+)为字段填充 padding,使每个 Cell 独占一个缓存行(x86 上 64 字节),避免伪共享:
伪共享(False Sharing):
两个 Cell 落在同一缓存行 → 线程 A 改 Cell[0] 使整个缓存行失效
→ 线程 B 改 Cell[1] 必须重新加载缓存行
→ 明明操作不同变量却互相拖慢
@Contended 解决:
Cell 前后填充 padding 到 64 字节 → 每个 Cell 独占缓存行
→ 各线程命中不同 Cell 时互不干扰3.3 add(long) 的三路选择
// java.util.concurrent.atomic.LongAdder
public void add(long x) {
Cell[] cs; long b, v; int m; Cell c;
if ((cs = cells) != null || !casBase(b = base, b + x)) { // 路径①
boolean uncontended = true;
if (cs == null || (m = cs.length - 1) < 0 ||
(c = cs[getProbe() & m]) == null || // 取命中的 Cell
!(uncontended = c.cas(v = c.value, v + x))) // CAS 该 Cell
longAccumulate(x, null, uncontended); // 慢路径
}
}add 的路径选择:
① 无 cells 且 casBase 成功 → 直接用 base(低竞争快路径)
② 有 cells → CAS 命中的 Cell
③ Cell CAS 失败 / cells 未初始化 → longAccumulate 慢路径:
- 初始化 cells(cellBusy 自旋锁 + CAS 控制)
- 扩容 cells(长度翻倍,直到 NCPU)
- 重新散列线程探针(getProbe 改变命中位置)
- 退化为 casBasegetProbe() & m 用线程本地探针散列到 Cell 槽位,longAccumulate 中还会在冲突时通过 ThreadLocalRandom 重置探针,让线程尽量分散到不同 Cell。
3.4 sum() 的最终求和
public long sum() {
Cell[] cs = cells;
long sum = base; // 先加基础值
if (cs != null) {
for (Cell c : cs) // 再累加所有 Cell
if (c != null) sum += c.value;
}
return sum;
}
sum()是弱一致性快照:累加过程中若有线程在写 Cell,结果可能略偏差(最后写入的 Cell 可能先于前面的 Cell 被读取)。对计数器统计场景(QPS、总请求数)完全够用;需要精确值用AtomicLong。
四、LongAccumulator 自定义累加
LongAdder 只能做加法,LongAccumulator 支持任意二元运算:
// java.util.concurrent.atomic.LongAccumulator
public LongAccumulator(LongBinaryOperator accumulatorFunction, long identity) {
this.function = accumulatorFunction;
base = this.identity = identity; // 初始值
}
public void accumulate(long x) {
Cell[] cs; long b, v, r; int m; Cell c;
if ((cs = cells) != null ||
(r = function.applyAsLong(b = base, x)) != b && !casBase(b, r)) {
boolean uncontended = true;
if (cs == null || (m = cs.length - 1) < 0 ||
(c = cs[getProbe() & m]) == null ||
!(uncontended = (r = function.applyAsLong(v = c.value, x)) == v
|| c.cas(v, r)))
longAccumulate(x, function, uncontended);
}
}
public long get() { return sum(); } // 汇总:对 base 和所有 Cell 应用函数// 使用示例:取最大值(identity 为最小值)
LongAccumulator max = new LongAccumulator(Math::max, Long.MIN_VALUE);
max.accumulate(42); max.accumulate(7); max.accumulate(99);
long result = max.get(); // 99原理同 LongAdder:Striped64 提供 base + Cell[] 的分散框架,accumulate 把每个值的计算交给 LongBinaryOperator,get() 用函数合并所有单元。
五、实现要点
原子类核心:
AtomicInteger:volatile 字段 + offset + CAS 自旋(getAndAddInt)
lazySet:putOrderedInt 懒写,低可见性高吞吐
AtomicReference:VarHandle CAS
FieldUpdater:反射取 offset,对已有类的 volatile 字段做 CAS
LongAdder:Striped64 的 base + Cell[] 分散计数
@Contended:缓存行填充防伪共享(64 字节对齐)
LongAccumulator:任意 LongBinaryOperator 累加
sum/get:弱一致快照
常见陷阱:
LongAdder.sum() 弱一致 → 需要精确值用 AtomicLong
伪共享未填充 → 高竞争下性能暴跌
FieldUpdater 字段必须 volatile
高竞争 CAS 自旋可能比锁更慢(考虑 LongAdder 或分段)