0%

LongAdder

LongAdder:高并发下的高效原子累加器

在高并发场景中,AtomicLong虽然能保证原子性,但大量线程竞争同一个变量时,CAS 操作的失败重试会导致CPU 自旋开销激增。JDK 8 引入的LongAdder通过分散热点的设计,显著提升了高并发下的累加性能,成为计数器场景的首选工具。

LongAdder 的核心设计思想

LongAdder的核心是分而治之:将一个全局计数器拆分为多个局部计数器(Cell数组),线程优先操作自己对应的局部计数器,最后通过累加局部计数器的值得到全局结果。

  • 低并发场景:直接操作base变量(类似AtomicLong);
  • 高并发场景:线程分散到不同的Cell中进行累加,减少竞争。

LongAdder 的内部结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class LongAdder extends Striped64 implements Serializable {
// 存储局部计数器的数组(延迟初始化)
transient volatile Cell[] cells;
// 基础值(低并发时直接使用,或作为备用)
transient volatile long base;
// 自旋锁标识(0:无锁;1:被占用,用于初始化/扩容cells或创建Cell)
transient volatile int cellsBusy;

// 局部计数器Cell类(使用@Contended避免伪共享)
@sun.misc.Contended
static final class Cell {
volatile long value; // 存储局部累加值
Cell(long x) { value = x; }
// CAS操作更新value
final boolean cas(long cmp, long val) {
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
}
// 省略Unsafe相关代码...
}
}

关键细节:

  • @Contended 注解:避免Cell数组元素因 CPU 缓存行共享导致的伪共享问题(提升缓存利用率);
  • volatile 修饰cellsbaseCell.value均用volatile保证可见性;
  • cellsBusy:通过 CAS 控制cells数组的初始化、扩容和Cell创建的原子性。

核心方法解析:add (long x)

add方法是LongAdder的核心,逻辑是优先操作 Cell,失败则退化为操作 base

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
// 情况1:cells未初始化,直接尝试更新base
if ((as = cells) == null || !casBase(b = base, b + x)) {
boolean uncontended = true;
// 情况2:cells已初始化,尝试更新当前线程对应的Cell
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x))) {
// 情况3:当前Cell竞争失败,进入复杂逻辑(初始化/扩容cells等)
longAccumulate(x, null, uncontended);
}
}
}

深度解析:longAccumulate 方法

longAccumulateLongAdder的灵魂,负责处理 Cell 的初始化、扩容、线程分配等复杂逻辑,确保高并发下的高效累加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) {
int h;
// 初始化线程探针(用于计算Cell索引)
if ((h = getProbe()) == 0) {
ThreadLocalRandom.current(); // 强制初始化探针
h = getProbe();
wasUncontended = true;
}
boolean collide = false; // 标记是否存在冲突
for (;;) { // 自旋重试直到成功
Cell[] as; Cell a; int n; long v;
if ((as = cells) != null && (n = as.length) > 0) {
// 分支1:cells已初始化,但当前线程对应的Cell为null
if ((a = as[(n - 1) & h]) == null) {
if (cellsBusy == 0) { // 无锁状态,尝试创建Cell
Cell r = new Cell(x);
if (cellsBusy == 0 && casCellsBusy()) { // 加锁
try {
// 再次检查(防止并发修改)
if ((as = cells) != null && (n = as.length) > 0 &&
as[(n - 1) & h] == null) {
as[(n - 1) & h] = r; // 放置新Cell
break;
}
} finally {
cellsBusy = 0; // 释放锁
}
}
}
collide = false;
}
// 分支2:当前Cell竞争失败,重置标志后重试
else if (!wasUncontended)
wasUncontended = true;
// 分支3:尝试CAS更新当前Cell的值
else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x))))
break;
// 分支4:Cell数组已达最大容量(>=CPU核心数)或已扩容,重置冲突标志
else if (n >= NCPU || cells != as)
collide = false;
// 分支5:标记冲突,准备扩容
else if (!collide)
collide = true;
// 分支6:扩容cells数组(当前容量未达上限且存在冲突)
else if (cellsBusy == 0 && casCellsBusy()) { // 加锁
try {
if (cells == as) { // 确认未被其他线程修改
Cell[] rs = new Cell[n << 1]; // 扩容为2倍
for (int i = 0; i < n; ++i)
rs[i] = as[i]; // 复制旧Cell
cells = rs;
}
} finally {
cellsBusy = 0; // 释放锁
}
collide = false;
continue; // 扩容后重试
}
// 重新计算探针,尝试分配到其他Cell
h = advanceProbe(h);
}
// 分支7:初始化cells数组(首次竞争时)
else if (cellsBusy == 0 && cells == as && casCellsBusy()) { // 加锁
try {
if (cells == as) {
cells = new Cell[2]; // 初始容量为2
cells[h & 1] = new Cell(x); // 分配当前线程到其中一个Cell
break;
}
} finally {
cellsBusy = 0; // 释放锁
}
}
// 分支8:cells正被初始化,退而更新base
else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x))))
break;
}
}

LongAdder 的性能优势与适用场景

性能优势

  • 分散竞争:高并发时线程操作不同的Cell,减少 CAS 失败次数;
  • 避免伪共享@Contended注解确保每个Cell独占缓存行,提升缓存效率;
  • 动态扩容:根据竞争程度自动扩容Cell数组(最大为 CPU 核心数),平衡空间与性能。

适用场景

  • 高并发计数器:如接口调用次数、用户访问量等统计场景;
  • 累加操作频繁,读取结果不频繁LongAddersum()方法需要遍历Cell数组累加,性能略低于AtomicLongget()

与 AtomicLong 的对比

特性 AtomicLong LongAdder
底层原理 单个变量的 CAS 操作 分散到多个 Cell 的 CAS 操作
高并发性能 较差(竞争激烈时自旋开销大) 优异(分散竞争)
内存占用 低(单个 long 变量) 高(动态扩容的 Cell 数组)
结果读取效率 高(直接返回变量值) 中(遍历 Cell 数组累加)
适用场景 低并发或需要精确实时结果 高并发且允许最终一致性结果

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10