为什么 LongAdder 在写热点非常高时比 AtomicLong 快?分析其分段计数与求和的折中设计。

浅静君_7672

浅静君_7672

2026-04-28

411人浏览

原创

longadder通过线程本地探针哈希定位cells槽位、@contended避免伪共享、懒初始化及2的幂长度实现写竞争分散;sum()需遍历cells故非o(1),且返回近似值,不适用于强一致性场景。

为什么 longadder 在写热点非常高时比 atomiclong 快?分析其分段计数与求和的折中设计。

LongAdder 的 cells 数组怎么分散写竞争?

它不靠锁或队列,而是用线程本地探针(ThreadLocalRandom.getProbe())算哈希,再对 cells.length - 1 取模定位槽位。每个线程尽量往自己“专属”的 Cell 上写,避免所有线程挤在同一个 base 地址上自旋。

关键点在于:Cell 类加了 @sun.misc.Contended 注解,强制每个 Cell 占满一个 CPU 缓存行(通常是 64 字节),彻底规避伪共享——否则哪怕映射到不同数组索引,只要落在同一缓存行,更新仍会互相失效、触发 MESI 协议广播。

  • 初始无竞争时,所有操作走 base,和 AtomicLong 行为一致
  • 第一次 CAS 失败后才懒初始化 cells,不是一上来就分配数组
  • cells 长度始终是 2 的幂,保证取模可用位运算(hash & (n-1)),更快

sum() 为什么不是 O(1),且不能频繁调用?

sum() 必须遍历整个 cells 数组,把每个 Cell.value 加上 base 才得出结果。这个过程不加锁、不阻塞写,所以返回的是某一时刻的近似快照,不是强一致值。

如果每毫秒都调 sum(),比如做实时监控轮询,开销会迅速上升:假设 cells 已扩容到 64 个,每次都要读 65 个 volatile long;而 AtomicLong.get() 是单次内存读,O(1) 且精确。

  • 高并发下 cells 可能达几十甚至上百项,sum() 耗时随数组长度线性增长
  • 若在 while (adder.sum() 循环里用它作退出条件,可能永远不退出——因为中间值滞后
  • 监控类场景建议每秒/每 5 秒调一次,而非毫秒级

为什么 LongAdder 不提供 compareAndSet()?

因为它压根没维护“当前全局精确值”这个概念。所有写操作分散在多个 Cell 上,没有单一权威 source of truth;sum() 是只读聚合,不可用于条件判断。

抖音实时热点榜
抖音实时热点榜

每小时追踪抖音热榜,抖音实时热点榜从创作者视角拆爆款、挖选题、预判趋势。

下载

如果你需要基于当前计数值做原子决策(比如“剩余库存 > 0 才扣减”),LongAdder 无法满足——它连“当前值”都定义不清。这种场景必须用 AtomicLong 或更高层的同步机制。

  • LongAdder 没有 compareAndSet()、getAndIncrement()、weakCompareAndSet() 等方法
  • 试图封装一个带 CAS 的 LongAdder 子类会破坏其设计前提,得不偿失
  • 真要混合读写逻辑,不如直接用 AtomicLong,或拆成“累加用 LongAdder + 决策用 AtomicLong”两套变量

低并发下 LongAdder 反而更慢?

没错。2~4 个线程持续更新时,LongAdder 的分支判断(先试 base,失败再找 cells)、probe 初始化、数组索引计算,都比 AtomicLong 直接一次 CAS 多几条指令。

它的优势只在“写远多于读 + 并发线程数明显超过 CPU 核心数”时才显现。实测中,8 线程以上持续递增,吞吐量通常拉开 3 倍起;但单线程下 LongAdder.increment() 比 AtomicLong.incrementAndGet() 慢 10%~15%。

  • 别为了“听起来高级”盲目替换,尤其在日志计数器、配置加载等低频场景
  • JVM 逃逸分析可能让 AtomicLong 的 volatile 读被优化,但 LongAdder 的数组访问几乎无法优化
  • 如果应用大部分时间处于低负载,突发高峰才高并发,LongAdder 的自适应扩容机制依然有效,不必担心冷启动问题

真正容易被忽略的是:它解决的是「写热点」,不是「读延迟」。你把 AtomicLong 换成 LongAdder 后,sum() 变慢了、语义变弱了、API 更受限了——这些代价不是 bug,是设计取舍。用之前先问一句:我到底在争什么?是每秒十万次 increment,还是每毫秒一次 get?

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

9437

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

6602

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

5872

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1024

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

868

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1236

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

2489

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

19831

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1115

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
光速学会docker容器
光速学会docker容器

共33课时 | 2.9万人学习

vue-cli4商城项目案例
vue-cli4商城项目案例

共12课时 | 3.9万人学习