应优先用LongAdder替代AtomicLong于写多读少、弱一致要求的计数场景;它通过分段Cell降低CAS冲突,提升高并发性能,但不可用于强一致性场景。

直接用 LongAdder 替代 AtomicLong,核心在于写多读少、不要求强一致的计数场景——它不靠“锁住一个数”,而是让线程分散到多个小格子(Cell)里各自加,最后再汇总。
什么时候该换 LongAdder?
出现这些信号就该考虑切换:
- 压测中
AtomicLong.incrementAndGet()的 CAS 失败率明显升高(比如每秒失败上万次) - 监控发现 CPU 使用率高但吞吐上不去,线程大量处于自旋状态
- QPS 超过几千,且计数器是全局共享、高频更新的(如 API 调用次数、错误打点、限流计数)
- 业务允许读取值有轻微延迟或近似性(比如监控大盘每 5 秒刷一次,不需要毫秒级精确)
怎么正确初始化和持有 LongAdder?
关键不是“怎么创建”,而是“怎么复用”:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 作为
static final字段长期持有,例如:private static final LongAdder REQUEST_COUNT = new LongAdder(); - 按维度隔离时,用
ConcurrentHashMap<String, LongAdder>缓存,key 是接口名、租户 ID 等,避免所有流量挤一个实例 - 不要在每次 HTTP 请求里
new LongAdder(),那会失去分段意义,还浪费内存 - 别把它放进
@RequestScope或ThreadLocal,否则退化成单线程计数,完全没用
读取和清零要注意什么?
sum() 和 sumThenReset() 行为不同,选错可能引发逻辑问题:
立即学习“Java免费学习笔记(深入)”;
-
sum()是最终一致性读:遍历当前所有活跃Cell并加上base,过程中其他线程可能正写入新值,结果是“近似总数”,适合监控告警 -
sumThenReset()是“先求和再清零”,但只对当前已存在的Cell生效;若有线程正在往新分配的Cell写,那些增量不会被重置,也不会被本次 sum 捕获 - 绝对不要用
LongAdder做库存扣减、订单号生成等需要严格原子性和顺序性的操作
它为什么比 AtomicLong 快?
本质是把“抢一个收银台”变成“每人去不同窗口”:
-
AtomicLong所有线程争同一个volatile long value,高并发下 CAS 频繁失败,CPU 空转+总线竞争严重 -
LongAdder初始只用base,竞争加剧后自动扩容cells数组(大小为 2 的幂),线程通过哈希定位到不同Cell更新,冲突大幅降低 -
Cell类自带@Contended注解,防止伪共享,进一步减少缓存行争用


















