LongAdder
你知道吗?AtomicLong 在高并发场景下,100 个线程同时累加,可能会执行数万次 CAS 操作。
因为每个线程都在自旋、重试、失败、再重试...
LongAdder 就是来解决这个问题的——它让每个线程累加到自己的"分段",最后汇总。
为什么需要 LongAdder
先看一个性能对比:
java
public class PerformanceComparison {
public static void main(String[] args) throws InterruptedException {
int threads = 100;
int ops = 100000;
// AtomicLong
AtomicLong atomicLong = new AtomicLong(0);
long start = System.nanoTime();
for (int i = 0; i < threads; i++) {
new Thread(() -> {
for (int j = 0; j < ops; j++) {
atomicLong.incrementAndGet();
}
}).start();
}
Thread.sleep(5000);
System.out.println("AtomicLong: " + atomicLong.get() +
", 耗时: " + (System.nanoTime() - start) / 1_000_000 + "ms");
// LongAdder
LongAdder longAdder = new LongAdder();
start = System.nanoTime();
for (int i = 0; i < threads; i++) {
new Thread(() -> {
for (int j = 0; j < ops; j++) {
longAdder.increment();
}
}).start();
}
Thread.sleep(5000);
System.out.println("LongAdder: " + longAdder.sum() +
", 耗时: " + (System.nanoTime() - start) / 1_000_000 + "ms");
}
}在高竞争下,LongAdder 通常比 AtomicLong 快 数倍到数十倍。
工作原理
┌────────────────────────────────────────────────────┐
│ LongAdder 分段累加 │
├────────────────────────────────────────────────────┤
│ │
│ 传统 CAS:所有线程争抢同一个变量 │
│ ┌─────────────────────────────────────────────┐ │
│ │ AtomicLong (全局竞争) │ │
│ │ T1 ──► [ ████████░░░░ ] T2 ──► │ │
│ │ T3 ──► [ ████████████ ] T4 ──► │ │
│ │ T5 ──► [ ██████████░░ ] T6 ──► │ │
│ └─────────────────────────────────────────────┘ │
│ 激烈竞争! │
│ │
│ LongAdder:每个线程累加到自己的 Cell │
│ ┌─────────────────────────────────────────────┐ │
│ │ LongAdder │ │
│ │ ┌─────────┬─────────┬─────────┬─────────┐ │ │
│ │ │ Cell[0] │ Cell[1] │ Cell[2] │ Cell[3] │ │ │
│ │ │ T1,T2 │ T3,T4 │ T5,T6 │ T7 │ │ │
│ │ │ 累加到这 │ 累加到这 │ 累加到这 │ 累加到这 │ │ │
│ │ └─────────┴─────────┴─────────┴─────────┘ │ │
│ └─────────────────────────────────────────────┘ │
│ 分段并行! │
│ │
└────────────────────────────────────────────────────┘核心思想:把一个value拆成多个,每个线程累加到自己的"分段",最后求和。
代码演示
基础用法
java
public class LongAdderDemo {
private static final LongAdder counter = new LongAdder();
public static void main(String[] args) throws InterruptedException {
int threadCount = 100;
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
counter.increment();
}
}).start();
}
Thread.sleep(2000);
System.out.println("计数结果: " + counter.sum()); // 应该是 100000
}
}常用方法
java
public class LongAdderMethods {
private static final LongAdder adder = new LongAdder();
public static void main(String[] args) {
// increment() - 递增1
adder.increment();
System.out.println("increment(): " + adder.sum()); // 1
// decrement() - 递减1
adder.decrement();
System.out.println("decrement(): " + adder.sum()); // 0
// add(long x) - 增加指定值
adder.add(100);
System.out.println("add(100): " + adder.sum()); // 100
// sum() - 获取当前总和
adder.increment();
adder.increment();
System.out.println("sum(): " + adder.sum()); // 102
// sumThenReset() - 获取总和并重置
long sum = adder.sumThenReset();
System.out.println("sumThenReset(): " + sum); // 102
System.out.println("重置后: " + adder.sum()); // 0
// reset() - 重置(线程不安全)
adder.add(50);
adder.reset();
System.out.println("reset() 后: " + adder.sum()); // 0
}
}实际应用:API 统计
java
public class ApiStatistics {
private final LongAdder totalRequests = new LongAdder();
private final LongAdder successCount = new LongAdder();
private final LongAdder errorCount = new LongAdder();
private final LongAdder latencyAdder = new LongAdder();
public void recordRequest(boolean success, long latencyMs) {
totalRequests.increment();
if (success) {
successCount.increment();
} else {
errorCount.increment();
}
latencyAdder.add(latencyMs);
}
public double getAverageLatency() {
long total = latencyAdder.sum();
long count = totalRequests.sum();
return count > 0 ? (double) total / count : 0;
}
public void printStats() {
System.out.println("=== API 统计 ===");
System.out.println("总请求数: " + totalRequests.sum());
System.out.println("成功数: " + successCount.sum());
System.out.println("失败数: " + errorCount.sum());
System.out.println("平均延迟: " + getAverageLatency() + "ms");
}
public static void main(String[] args) {
ApiStatistics stats = new ApiStatistics();
Random random = new Random();
// 模拟 10000 次请求
for (int i = 0; i < 10000; i++) {
boolean success = random.nextInt(100) > 5; // 95% 成功率
long latency = 10 + random.nextInt(100);
stats.recordRequest(success, latency);
}
stats.printStats();
}
}LongAccumulator:自定义累加
LongAdder 的增强版,支持自定义累加函数:
java
public class LongAccumulatorDemo {
public static void main(String[] args) {
// 求最大值
LongAccumulator maxAccumulator = new LongAccumulator(
Long::max, // 累加函数
Long.MIN_VALUE // 初始值
);
maxAccumulator.accumulate(10);
maxAccumulator.accumulate(5);
maxAccumulator.accumulate(20);
System.out.println("最大值: " + maxAccumulator.get()); // 20
// 求最小值
LongAccumulator minAccumulator = new LongAccumulator(
Long::min,
Long.MAX_VALUE
);
minAccumulator.accumulate(10);
minAccumulator.accumulate(5);
minAccumulator.accumulate(20);
System.out.println("最小值: " + minAccumulator.get()); // 5
}
}LongAdder vs AtomicLong
| 特性 | AtomicLong | LongAdder |
|---|---|---|
| 原理 | CAS 自旋 | 分段累加 |
| 竞争 | 全局竞争 | 分段并行 |
| 性能 | 高竞争时差 | 高竞争时优 |
| 精度 | 精确 | 精确(sum时汇总) |
| 内存 | 一个变量 | 多个 Cell |
| sum() | get() | 可能非原子 |
注意事项
- sum() 可能不精确:sum() 在汇总过程中可能有新值正在累加,结果是近似值
- 不适合需要精确中间值的场景:LongAdder 设计目标是吞吐量
- 适合统计场景:QPS、计数、延迟统计
- reset() 线程不安全:多线程下 reset() 可能丢失其他线程的累加
选择建议
需要精确值吗?
├── 是 ──► AtomicLong
└── 否 ──► 极高并发吗?
├── 是 ──► LongAdder
└── 否 ──► AtomicLong