longAdder
LongAdder 是 Java 8 引入的一个用于高并发环境下进行求和的类
如果有多个线程频繁地对同一个数字进行加减操作(比如计步器、QPS 统计),LongAdder 通常比传统的 AtomicLong 性能更好。
为什么需要 LongAdder?
- AtomicLong 的瓶颈:
AtomicLong内部使用的是 CAS (Compare-And-Swap) 操作。在高度并发的情况下,大量线程会同时竞争修改同一个变量。因为每次只有一个线程能成功,其他线程会不断自旋重试,这会导致严重的 CPU 资源浪费和性能下降。
LongAdder 的核心原理:分段锁(热点分散)
LongAdder 的设计思路非常聪明:既然大家都争抢同一个变量,那我就把这个变量拆成多个。
- 分散热点:
LongAdder内部维护了一个base变量和一个Cell[]数组。 - 无竞争时: 线程直接累加到
base变量上。 - 有竞争时: 不同的线程会被映射到
Cell[]数组的不同索引位置,各自累加自己的Cell值。 - 最后求和: 当你需要获取最终的总和时,调用
sum()方法,它会将base和所有Cell里的值加起来返回。
比喻:
AtomicLong就像是全班只有一个笔记本,所有人排队等着在那一页纸上写数字;LongAdder则是给每个人发一张小纸条写,最后老师把所有纸条收上来算个总数。
主要 API
| 方法 | 说明 |
|---|---|
add(long x) | 将当前值增加 x |
increment() | 自增 1 |
decrement() | 自减 1 |
sum() | 返回当前的总和(注意:这个值不一定是强一致性的) |
reset() | 将所有值清零 |
sumThenReset() | 获取总和并清零,常用于周期性统计 |
性能对比与使用场景
1. 性能
- 低并发:
AtomicLong和LongAdder差别不大,甚至AtomicLong略快。 - 高并发:
LongAdder的吞吐量远高于AtomicLong。
2. 如何选择?
- 选
AtomicLong: 如果你需要精确的 CAS 语义(比如compareAndSet操作),或者你需要实时保证数值的绝对准确性。 - 选
LongAdder: 如果你的场景是高频写入、低频读取的统计逻辑(如监控数据、网站点击量),且不需要compareAndSet这种原子操作。
总结
LongAdder 是通过空间换时间的典型案例。它利用分段增加的方式减少了线程竞争,是处理高并发计数任务的首选利器。不过要记住,它的 sum() 结果是一个“最终一致性”的值,在求和瞬间如果有线程正在写入,结果可能会有微小偏差。
它只适合用来做监控、统计、计数