直接用 longadder 替换 atomiclong 可显著提升高并发计数性能,因其将单变量拆为多独立槽位,减少 cas 自旋;适用于只累加、不强读、写远多于读的场景。

直接用 LongAdder 替换 AtomicLong 就能显著提升高并发计数性能,核心在于它把“一个热点变量”拆成“多个独立单元”,让线程各自操作不冲突的槽位,避免 CAS 自旋浪费 CPU。
适用场景:只累加、不强读、写远多于读
不是所有计数都适合换 LongAdder。它专为以下情况设计:
- 只做
increment()、add(x)这类累加/累减,不依赖中间精确值(比如统计总请求数、错误次数、QPS) - 更新非常频繁,但调用
sum()的次数很少(例如每分钟汇总一次,而非每次请求都查) - 允许最终一致:
sum()返回的是当前所有槽位的快照和,不是某个原子时刻的精确值(并发写入中调用可能略滞后) - 不能用于控制逻辑,比如
while (counter.sum() 这种循环条件——因为值非实时,可能导致死循环或漏判
替换方式:简单但注意语义变化
代码改动极小,但关键细节不能忽略:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 声明从
private final AtomicLong counter = new AtomicLong();改为private final LongAdder counter = new LongAdder(); -
counter.incrementAndGet()→counter.increment();(increment()无返回值) -
counter.get()→counter.sum()(注意:不是get(),且结果非强一致) - 如果需要初始化非零值,不能直接构造,得先
new LongAdder()再add(initValue)
原理简析:分段 + 避伪共享 = 减竞争
它不是靠锁或单点 CAS,而是靠两层机制降低争抢:
-
base + cells 分治结构:低并发时所有线程尝试更新公共
base;一旦发现竞争(CAS 失败多次),就扩容cells数组,线程按哈希分散到不同Cell槽位写入,彼此互不干扰 -
@Contended 填充防伪共享:每个
Cell内部用 JVM 注解自动填充字节,确保每个Cell.value单独占满一个 CPU 缓存行(64 字节),避免不同核修改相邻变量时引发缓存行反复同步
实际效果:线程越多,优势越明显
在 256 线程并发递增场景下,LongAdder 耗时约 220ms,而 AtomicLong 达 5800ms —— 性能差距达 26 倍。这不是理论优化,是真实可测的吞吐提升。但也要注意:线程数少(如 ≤4)时,AtomicLong 反而更轻量;若需高频读取累计值,LongAdder 的遍历求和开销会抵消部分写入优势。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










