LongAdder 原理与应用详解
一、设计背景与核心思想
1. 传统原子类的性能瓶颈
- AtomicInteger/AtomicLong 基于 CAS 实现
- 高并发场景缺陷:
- CAS 失败率随竞争加剧指数上升
- CPU 空转消耗大量资源
- 缓存一致性流量(MESI协议)导致总线风暴
2. LongAdder 设计目标
- 降低竞争:通过数据分片分散写压力
- 空间换时间:牺牲部分内存换取更高吞吐
- 最终一致性:允许读取结果存在短暂误差
二、实现原理剖析
1. 核心数据结构
transient volatile long base;
transient volatile Cell[] cells;
@jdk.internal.vm.annotation.Contended
static final class Cell {volatile long value;Cell(long x) { value = x; }
}
2. 分段累加流程
3. 伪共享解决方案
- 问题根源:不同线程的变量共享同一缓存行(通常 64 字节)
- 优化方案:
三、关键操作解析
1. 累加操作(add)
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 ||!(uncontended = c.cas(v = c.value, v + x)))longAccumulate(x, null, uncontended);}
}
- 执行策略:
- 优先尝试更新 base
- 失败后定位到线程对应的 Cell
- 多级失败后触发数组扩容
2. 取值操作(sum)
public long sum() {Cell[] cs = cells;long sum = base;if (cs != null) {for (Cell c : cs)if (c != null) sum += c.value;}return sum;
}
- 特点:
- 非原子快照(可能包含进行中的更新)
- 时间复杂度 O(n)(需遍历所有 Cell)
四、示例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;public class SimpleLongAdderExample {public static void main(String[] args) throws InterruptedException {LongAdder counter = new LongAdder();ExecutorService executor = Executors.newFixedThreadPool(10);for (int i = 0; i < 100; i++) {executor.submit(() -> {for (int j = 0; j < 1000; j++) {counter.increment(); }});}executor.shutdown();executor.awaitTermination(1, TimeUnit.MINUTES);System.out.println("最终计数: " + counter.sum()); }
}
五、性能对比数据
测试环境:
- CPU:8 核 Intel i9-9900K
- 内存:32GB DDR4
- JVM:OpenJDK 17
- 测试用例:32 线程执行 1 亿次累加
实现方案 | 耗时 (ms) | 吞吐量 (ops/ms) | 内存占用 |
---|
synchronized | 4,520 | 22,123 | 低 |
AtomicLong | 1,280 | 78,125 | 低 |
LongAdder | 235 | 425,531 | 中 |
ThreadLocal 优化 | 182 | 549,450 | 高 |
六、应用场景指南
1. 推荐使用场景
场景类型 | 典型用例 | 优势说明 |
---|
高频计数器 | 网站 PV/UV 统计 | 分散写竞争 |
监控指标采集 | QPS/TPS 统计 | 允许最终一致性 |
分布式限流 | 令牌桶算法实现 | 避免 CAS 失败风暴 |
大数据聚合 | 实时计算中间结果 | 支持快速并行累加 |
2. 不适用场景
场景类型 | 典型用例 | 问题分析 |
---|
精确原子操作 | 库存扣减 | sum() 非原子快照 |
读多写少 | 配置项更新 | AtomicLong 更高效 |
内存敏感场景 | 海量独立计数器 | Cell 数组内存开销大 |
六、实现原理总结
设计要点 | 实现方案 | 解决的问题 |
---|
竞争分散 | 分片 Cell 数组 | 降低 CAS 失败率 |
伪共享预防 | @Contended 注解 | 提升缓存利用率 |
动态扩容 | 按需创建 Cell | 平衡性能与内存 |
延迟初始化 | 初始使用 base 变量 | 减少内存开销 |
最终一致性 | sum() 合并所有 Cell | 保证最终结果正确性 |
通过理解 LongAdder 的设计哲学和实现细节,开发者可以在高并发场景中做出更优的技术选型,在保证线程安全的前提下实现 5-10 倍的性能提升。关键是要根据实际业务场景的读写比例、一致性要求和资源限制进行合理选择。