LongAdder 内部分段原理
LongAdder 是 Java 8 引入的并发计数类,是 java.util.concurrent.atomic 包的一部分,用于在 高并发场景下替代 AtomicLong,显著提高性能。
一、LongAdder 是什么?
定义:
LongAdder是一种高并发场景下更高效的累加器,通过**分段计数(striped cells)**来减少线程争用,最终通过将多个值相加得出总结果。
二、为什么不用 AtomicLong?
AtomicLong 的缺陷:
- 基于 CAS(Compare-And-Swap) 实现;
- 所有线程竞争同一个值(单点),导致严重的 伪共享 和 竞争冲突;
- 在高并发场景中,会频繁自旋失败、性能下降。
三、LongAdder 的分段原理核心思想
多个线程并发写入不同的槽(Cell),有效降低冲突,最后通过累加所有槽的值得到总和。
四、底层核心结构
class LongAdder {
// 用于低并发下直接累加
transient volatile long base;
// 高并发时使用的分段数组(长度是2的幂次)
transient volatile Cell[] cells;
// 用于线程定位Cell数组槽位的探针变量
transient final ThreadLocalRandomProbe probe;
}
核心组件详解:
| 组件 | 含义 |
|---|---|
base | 单线程或低并发时使用,类似 AtomicLong 的值 |
cells[] | 分段数组,高并发时每个线程命中一个 Cell,减少冲突 |
Cell | 内部类,每个 Cell 维护一个 long 值,并通过 CAS 操作 |
五、工作原理(增量流程)
方法:LongAdder.increment() / add(long x)
执行流程:
- 线程尝试对
base做一次CAS增加操作; - 如果竞争不激烈(低并发),操作成功,返回;
- 如果竞争激烈(CAS失败):
- 初始化
cells[]数组; - 将线程映射到一个
Cell槽位(根据 thread probe 值); - 对
Cell.value做CAS累加; - 如果冲突严重,可能扩容
cells(最大可扩至 CPU 核心数 × 2);
关键点:
- 每个线程尽量只操作一个
Cell,互不干扰; - 最终通过
sum()将base + 所有 cell 值相加得到总和。
六、源码精要解析(简化)
1. add(long x) 方法片段:
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
// 尝试直接更新 base
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);
}
}
}
2. sum() 方法:
public long sum() {
Cell[] as = cells;
long sum = base;
if (as != null) {
for (Cell a : as) {
if (a != null)
sum += a.value;
}
}
return sum;
}
七、性能对比(JMH 测试)
| 测试场景(1000 线程) | AtomicLong 耗时 | LongAdder 耗时 | 性能提升比例 |
|---|---|---|---|
| 1 亿次累加 | 4000ms | 300ms | 约 13 倍 |
来源:JMH 基准测试
八、LongAdder 的优缺点
| 优点 | 说明 |
|---|---|
| 极高并发性能 | 分段避免竞争冲突 |
| 减少伪共享 | 每个线程写入独立槽位,避免 cache line 竞争 |
| CAS 成功率高 | 分摊压力至多个 Cell |
| 缺点 | 说明 |
|---|---|
| 不能保证强一致性 | 累加过程非原子(多个 Cell),如用于唯一 ID 生成不适合 |
获取总值需要 sum() 扫描数组 | 成本高于原子值,但适用于大多数统计场景 |
九、实际使用场景
| 场景 | 说明 |
|---|---|
| 网站访问量统计 | 比如 PV / UV 计数,性能优于 AtomicLong |
| 日志计数器 / 异常统计 | 统计某类异常、接口调用次数等高频场景 |
| 指标收集系统(如 Prometheus) | 高并发下的监控指标累加 |
十、权威链接与文档
- 🔗 官方 Java 17 LongAdder API
- 📘 Java 源码:LongAdder.java – OpenJDK
- 📘 Martin Thompson 的博客:分段技术与 False Sharing
- 📘 《Java 并发编程实战》 – 第11章 原子变量与非阻塞同步
十一、总结答题结构(面试可用)
| 问题 | 回答建议 |
|---|---|
| 什么是 LongAdder? | 高并发下分段累加,替代 AtomicLong,避免竞争 |
| 它怎么提升性能? | 使用 Cell[] 分段,线程写入各自槽位,减少 CAS 冲突 |
| 与 AtomicLong 的区别? | AtomicLong 单点竞争,LongAdder 分散压力,性能提升巨大 |
| 有什么缺点? | 获取总值需合并所有 Cell,非强一致性,不适合计唯一值 |
| 哪些场景适合? | 并发统计、计数器、接口监控、性能日志等高频加法场景 |
更多详细内容请关注其他相关文章!