应使用 longadder 替代 atomiclong 而非 atomicinteger,因其为 long 类型且 jdk 未提供 intadder;longadder 通过分段 cell 数组降低 cas 竞争,适用于写多读少、允许最终一致性的统计场景,如 qps 计数、错误统计。

直接用 LongAdder 替代 AtomicInteger 并不推荐——因为类型不匹配,且语义不同。正确做法是:用 LongAdder 替代 AtomicLong,或用 IntAdder(不存在)的等效思路转为 LongAdder + 业务层面 int 转换;但更常见、更稳妥的是统一升级为 LongAdder,再按需截断或约束范围。
为什么不能直接“代替 AtomicInteger”
AtomicInteger 是 int 类型,LongAdder 是 long 类型,二者不兼容;JDK 没提供 IntAdder。强行用 LongAdder 存储 int 值虽可行(如只记录 0–100 万的计数),但本质是借用 long 容量模拟 int 行为,并非类型替代。真正要解决“多核写热点”,关键是替换掉单点竞争的原子整数类,而不是拘泥于 int/long 表面类型。
LongAdder 如何化解写热点
它把单一 value 拆成一个分段数组(Cell[]),每个线程通过探针哈希定位到专属槽位更新,避免所有线程争抢同一内存地址:
- 无竞争时,直接更新 base 值(类似 AtomicLong)
- 发生竞争时,自动扩容 cells 数组,分散写压力
- 线程间更新彼此隔离,CAS 失败率大幅下降,消除恶性自旋
- 读取总和需遍历所有槽位累加,因此不是实时快照,而是最终一致
实际替换步骤与注意事项
以统计请求总数为例,从 AtomicLong 迁移到 LongAdder 很轻量:
- 声明改为
private final LongAdder counter = new LongAdder(); - 写操作从
counter.incrementAndGet()改为counter.increment();(无返回值) - 读操作从
counter.get()改为counter.sum()(注意:sum() 不是原子快照,高并发中可能略滞后) - 若需重置,调用
counter.reset()(清空并归零,比新建对象更高效)
什么场景适合切换
满足以下三点才建议替换:
- 纯累加/累减用途,比如 QPS 统计、错误计数、日志打点次数
- 写操作远多于读操作(例如每毫秒更新千次,每秒只查一次 sum)
- 能接受 sum() 结果存在微小延迟(例如某次调用漏掉正在写入的线程值,但几毫秒后就准确)
不适合的场景包括:生成唯一序号、作为 while 循环终止条件、要求每次读都精确反映最新写入状态。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











