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; transient volatile int cellsBusy;
@sun.misc.Contended static final class Cell { volatile long value; Cell(long x) { value = x; } final boolean cas(long cmp, long val) { return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); } } }
|
关键细节:
- @Contended 注解:避免
Cell数组元素因 CPU 缓存行共享导致的伪共享问题(提升缓存利用率);
- volatile 修饰:
cells、base、Cell.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; if ((as = cells) == null || !casBase(b = base, b + x)) { boolean uncontended = true; if (as == null || (m = as.length - 1) < 0 || (a = as[getProbe() & m]) == null || !(uncontended = a.cas(v = a.value, v + x))) { longAccumulate(x, null, uncontended); } } }
|
深度解析:longAccumulate 方法
longAccumulate是LongAdder的灵魂,负责处理 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; 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) { if ((a = as[(n - 1) & h]) == null) { if (cellsBusy == 0) { 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; break; } } finally { cellsBusy = 0; } } } collide = false; } else if (!wasUncontended) wasUncontended = true; else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x)))) break; else if (n >= NCPU || cells != as) collide = false; else if (!collide) collide = true; else if (cellsBusy == 0 && casCellsBusy()) { try { if (cells == as) { Cell[] rs = new Cell[n << 1]; for (int i = 0; i < n; ++i) rs[i] = as[i]; cells = rs; } } finally { cellsBusy = 0; } collide = false; continue; } h = advanceProbe(h); } else if (cellsBusy == 0 && cells == as && casCellsBusy()) { try { if (cells == as) { cells = new Cell[2]; cells[h & 1] = new Cell(x); break; } } finally { cellsBusy = 0; } } else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x)))) break; } }
|
LongAdder 的性能优势与适用场景
性能优势
- 分散竞争:高并发时线程操作不同的
Cell,减少 CAS 失败次数;
- 避免伪共享:
@Contended注解确保每个Cell独占缓存行,提升缓存效率;
- 动态扩容:根据竞争程度自动扩容
Cell数组(最大为 CPU 核心数),平衡空间与性能。
适用场景
- 高并发计数器:如接口调用次数、用户访问量等统计场景;
- 累加操作频繁,读取结果不频繁:
LongAdder的sum()方法需要遍历Cell数组累加,性能略低于AtomicLong的get()。
与 AtomicLong 的对比
| 特性 |
AtomicLong |
LongAdder |
| 底层原理 |
单个变量的 CAS 操作 |
分散到多个 Cell 的 CAS 操作 |
| 高并发性能 |
较差(竞争激烈时自旋开销大) |
优异(分散竞争) |
| 内存占用 |
低(单个 long 变量) |
高(动态扩容的 Cell 数组) |
| 结果读取效率 |
高(直接返回变量值) |
中(遍历 Cell 数组累加) |
| 适用场景 |
低并发或需要精确实时结果 |
高并发且允许最终一致性结果 |
v1.3.10