首页/新闻资讯/正文详情

【HUC】原子类 LongAdder:从 add() 到 longAccumulate() 源码解析

发布时间:2026/11/15 12:12:06 来源:云帆数科 栏目:资讯中心
【HUC】原子类 LongAdder:从 add() 到 longAccumulate() 源码解析
LongAdder的设计哲学是用空间换时间通过分散热点来应对高并发。add()方法作为入口其逻辑清晰地体现了这一思想尽可能在无竞争或低竞争路径上快速返回将复杂的竞争处理“委托”给longAccumulate()。add(long x)快速路径与竞争委托LongAdder本身没有定义add它继承自Striped64。add方法的代码不长但每一行都对应着一种并发场景。publicvoidadd(longx){Cell[]as;longb,v;intm;Cella;// 条件一cells 已初始化 (as ! null)// 条件二!casBase(b base, b x) 意味着对 base 的 CAS 更新失败if((ascells)!null||!casBase(bbase,bx)){booleanuncontendedtrue;// 初始假定无竞争// 进入此分支意味着要么 cells 已存在要么 base 的 CAS 失败了// 条件一cells 为 nullbase CAS 失败但 cells 尚未初始化// 条件二cells 长度为 0理论上不应发生// 条件三当前线程 hash 命中的 Cell 为 null需要新建 Cell// 条件四!a.cas(v a.value, v x) 对命中的 Cell 进行 CAS 更新失败if(asnull||(nas.length)1||(aas[(n-1)(mgetProbe())])null||!(uncontendeda.cas(va.value,vx)))longAccumulate(x,null,uncontended);}}执行流程解读低竞争快路径如果cells尚未初始化as null方法会首先尝试对全局的base进行 CAS 累加casBase。在并发度极低时这一步就能成功add方法直接返回没有任何额外开销。base CAS 失败 → 触发复杂逻辑如果cells已经初始化或者base的 CAS 操作失败说明有竞争就会进入if块准备在cells数组上操作。Cell 层面的快速路径在进入longAccumulate之前add会做最后一次“挣扎”。它通过getProbe()获取当前线程的探测哈希值m定位到cells数组中对应的槽位as[(n-1) m]。如果该槽位已经存在一个Cell对象它会尝试直接对这个Cell的value进行 CAS 累加。这次 CAS 的结果会被记录在uncontended变量中。如果这次 CAS 成功方法同样直接返回无需进入更重量级的longAccumulate。委托给longAccumulate只有当出现以下任一情况时才会调用longAccumulatecells数组尚未初始化但baseCAS 已失败。线程命中的槽位为null需要创建新的Cell。对已存在的Cell进行 CAS 更新失败uncontended false表明该Cell也出现了竞争。关键点add方法通过casBase和Cell.cas两次尝试尽可能避免进入longAccumulate。传入的wasUncontended参数即uncontended变量告知了longAccumulate“在你接手之前我已经在 Cell 上尝试过 CAS 并且失败了”这对于longAccumulate的后续决策至关重要。longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended)竞争处理核心这个方法由Striped64提供是处理初始化、创建Cell、扩容以及解决竞争的总控逻辑。在LongAdder的调用中fn始终为null代表简单的加法但该方法也服务于支持自定义函数的LongAccumulator。finalvoidlongAccumulate(longx,LongBinaryOperatorfn,booleanwasUncontended){inth;// 1. 确保当前线程拥有一个非零的 probe 哈希值if((hgetProbe())0){ThreadLocalRandom.current();// 强制初始化线程的 probehgetProbe();wasUncontendedtrue;// 新分配 probe视为无竞争}booleancollidefalse;// 扩容意向标志for(;;){// 2. 无限自旋直至成功Cell[]as;Cella;intn;longv;// CASE 1: cells 数组已初始化if((ascells)!null(nas.length)0){// ...}// CASE 2: cells 未初始化尝试初始化elseif(cellsBusy0cellsascasCellsBusy()){// ...}// CASE 3: 回退直接累加到 baseelseif(casBase(vbase,((fnnull)?vx:fn.applyAsLong(v,x))))break;}}longAccumulate的主体是一个for (;;)自旋循环它的内部被划分为三个主要的CASE分别对应cells的不同状态。前置操作线程 Probe 的初始化getProbe()返回当前线程的探测哈希值probe。如果为0意味着该线程尚未被分配probe。此时会调用ThreadLocalRandom.current()强制初始化并重新获取probe。同时wasUncontended被强制设为true。原因是一个新分配probe的线程其哈希值默认会映射到cells[0]这不被视为一次真正的竞争失败因此重置竞争标志给它一次“干净”的重试机会。CASE 1cells已初始化 —— 创建Cell、重试或扩容这是最复杂的路径当cells数组存在且长度大于0时进入。if((ascells)!null(nas.length)0){// CASE 1.1: 命中的槽位为 null需要创建新 Cellif((aas[(n-1)h])null){if(cellsBusy0){// 无锁状态CellrnewCell(x);// 乐观地预先创建if(cellsBusy0casCellsBusy()){// 获取锁booleancreatedfalse;try{Cell[]rs;intm,j;// 双重检查防止在获取锁期间其他线程已在该位置创建了 Cellif((rscells)!null(mrs.length)0rs[j(m-1)h]null){rs[j]r;createdtrue;}}finally{cellsBusy0;// 释放锁}if(created)break;// 创建成功退出自旋continue;// 槽位已被其他线程占用重试}}collidefalse;// 命中空槽位不需要扩容}// CASE 1.2: 之前已知 CAS 失败重置标志后重试elseif(!wasUncontended)wasUncontendedtrue;// 继续循环使用重新哈希后的 probe 重试// CASE 1.3: 对命中的 Cell 进行 CAS 更新elseif(a.cas(va.value,((fnnull)?vx:fn.applyAsLong(v,x))))break;// 更新成功退出自旋// CASE 1.4: 已达到扩容上限或 cells 已被其他线程修改放弃本次扩容意图elseif(nNCPU||cells!as)collidefalse;// CASE 1.5: 标记扩容意向但尚未真正扩容elseif(!collide)collidetrue;// CASE 1.6: 真正执行扩容elseif(cellsBusy0casCellsBusy()){try{if(cellsas){// 再次检查防止重复扩容Cell[]rsnewCell[n1];// 容量翻倍for(inti0;in;i)rs[i]as[i];// 迁移已有 Cellcellsrs;}}finally{cellsBusy0;}collidefalse;continue;// 扩容后重试}hadvanceProbe(h);// ★ 关键一步重新计算当前线程的 probe}CASE 1 的逻辑链条CASE 1.1空槽位当前线程的probe映射到一个null槽位。此时会在cellsBusy自旋锁的保护下通过双重检查rs[j] null安全地创建一个新的Cell并放入。cellsBusy是一个通过 CAS 维护的自旋锁用于保护cells数组的结构性修改初始化、扩容、创建 Cell。CASE 1.2重置竞争标志如果add方法传入的wasUncontended为false表示在add中 Cell CAS 已失败这里会将其重置为true。这相当于给该线程一次“申诉”机会之前失败是因为probe对应的 Cell 有竞争现在我会重新计算probe在最后一行advanceProbe(h)换一个 Cell 再试。CASE 1.3Cell CAS 成功对命中的Cell的value进行 CAS 累加。这是最常见的成功路径成功后直接break退出自旋。CASE 1.4 - 1.6扩容决策如果n NCPU数组长度已达 CPU 核数上限或cells引用已发生变化则放弃扩容collide false。如果collide为false将其设为true。这是一个扩容意向标记表示“我刚刚在这个 Cell 上又失败了可能真的需要扩容了”。如果collide已经是true意味着上次失败后重试仍然失败并且成功获取了cellsBusy锁则真正执行扩容将数组长度翻倍n 1并迁移所有已有的Cell引用。h advanceProbe(h)无论本轮CASE 1的哪个分支被执行除了成功break或continue到扩容外最后都会执行这行代码。它使用一个伪随机函数重新计算当前线程的probe。这是实现“分散热点”的核心机制——当在一个Cell上竞争失败后线程会“换一个地方”再试而不是死磕同一个槽位。CASE 2cells未初始化 —— 安全地完成初始化当cells为null或长度为0且cellsBusy锁可用时进入此分支。elseif(cellsBusy0cellsascasCellsBusy()){booleaninitfalse;try{// 双重检查防止在获取锁期间其他线程已完成初始化if(cellsas){Cell[]rsnewCell[2];// 初始容量为 2rs[h1]newCell(x);// 将当前线程的增量放入对应的 Cellcellsrs;inittrue;}}finally{cellsBusy0;}if(init)break;// 初始化成功退出自旋}这是cells数组的懒初始化路径。只有在baseCAS 失败后add才会委托到这里。初始化过程同样受cellsBusy锁保护并包含cells as的双重检查以确保并发环境下初始化操作的原子性。初始数组长度为2当前线程的增量x会被直接放入rs[h 1]对应的Cell中。CASE 3回退 —— 直接更新base这是最后的保底路径。elseif(casBase(vbase,((fnnull)?vx:fn.applyAsLong(v,x))))break;当CASE 1cells存在但处理失败和CASE 2无法获取锁来初始化cells都走不通时意味着当前有另一个线程正在持有cellsBusy锁可能在初始化或扩容或者cells已被其他线程初始化。此时当前线程会选择回退到最基础的base上进行 CAS 累加。如果这个 CAS 成功就退出自旋如果失败则继续循环重试。 核心机制与设计思想总结从add到longAccumulate的整个流程贯穿了以下几个关键设计分层竞争处理性能敏感路径被极致优化。baseCAS →CellCAS →longAccumulate每一层都试图以最低成本解决问题将重量级操作限制在真正需要的时候。cellsBusy自旋锁一个轻量级的 CAS 锁仅用于保护cells数组的结构性修改初始化、扩容、创建Cell。对Cell内部value的更新并不需要此锁而是通过Cell自身的 CAS 完成。advanceProbe重哈希这是“分散”策略的引擎。在竞争失败或创建新Cell后线程的probe会变化从而在后续重试中可能命中cells数组中的不同槽位动态地分散访问压力。有界扩容 (n NCPU)cells数组的长度不会无限增长其上限是 CPU 核数。这避免了过度扩容带来的内存和缓存开销因为在核数已满的情况下更多的槽位也无法带来真正的并行度提升。Cell的缓存行填充Cell是AtomicLong的一个“填充变体”Padded variant。它的value字段前后有额外的填充字段目的是防止伪共享False Sharing。这确保了对不同Cell的更新不会因为落在同一个 CPU 缓存行上而相互干扰是高性能的关键底层优化。sum()的结果最终LongAdder.sum()返回的是base的值与cells数组中所有非null的Cell的value之和。需要注意sum()方法在并发更新时不保证原子性它返回的是一个“弱一致”的快照值。LongAdder通过这套精密的协作机制在高并发统计求和的场景下实现了远超AtomicLong的吞吐量其代价是在空间Cell数组、填充和读取时的数据一致性sum()非原子上做出了权衡。相关方法和变量定义流程图

相关推荐

高通SA8775P与SA8797P深度对比:从48 TOPS到320 TOPS,CPU、GPU、NPU、DDR、ISP、PCIe及舱驾融合架构全面解析
高通SA8775P与SA8797P深度对比:从48 TOPS到320 TOPS,CPU、GPU、NPU、DDR、ISP、PCIe及舱驾融合架构全面解析

高通SA8775P与SA8797P深度对比:从48 TOPS到320 TOPS,CPU、GPU、NPU、DDR、ISP、PCIe及舱驾融合架构全面解析 图1|未来智能座舱:高性能SoC正在驱动多屏交互、AI计算与舱驾融合 一、为什么SA8775P与SA8797P值得研究? 汽车电子电气架构正在从分布式ECU向域集中、中央计算发… · 2026/11/15 12:12:06

在 Python 编程中,“逆算子”并不是一个严格的内置术语,它通常可以理解为:**对某个运算或算子求逆**
在 Python 编程中,“逆算子”并不是一个严格的内置术语,它通常可以理解为:**对某个运算或算子求逆**

在 Python 编程中,“逆算子”并不是一个严格的内置术语,它通常可以理解为:对某个运算或算子求逆。例如,加法有逆运算减法,乘法有逆运算除法,函数映射有反函数,线性变换有逆矩阵,离散… · 2026/11/15 12:12:06

正定算子是线性代数、优化理论、数值计算和机器学习中非常重要的概念
正定算子是线性代数、优化理论、数值计算和机器学习中非常重要的概念

正定算子是线性代数、优化理论、数值计算和机器学习中非常重要的概念。在线性代数中,正定算子通常对应正定矩阵;在泛函分析或量子力学中,它也可以指希尔伯特空间上的正定线性算子。对于有限维实向量空间,一个对称矩阵如果对所有非… · 2026/11/12 4:51:35

日照电工证去哪办理怎么查网上查询结果
日照电工证去哪办理怎么查网上查询结果

日照电工证去哪办理怎么查网上查询结果 你是不是也担心自己学历不高,考不了电工证?别慌, 日照电工证去哪办理 这件事,其实没你想象的那么复杂,关键是知道怎么操作和 网上查询… · 2026/11/15 12:11:20

青岛高压电工作业通过率揭秘:实操考试心里没底怕挂科怎么办
青岛高压电工作业通过率揭秘:实操考试心里没底怕挂科怎么办

青岛高压电工作业通过率揭秘:实操考试心里没底怕挂科怎么办 实操考试心里没底怕挂科?尤其是像你这样刚毕业的周口工程类学生,第一次接触高压电工作业证考试,心里难免会打鼓。别急,今天咱们就来聊聊 青岛高压电工作业… · 2026/11/15 12:11:20

网上起电工证可靠吗?学历不高怕报不上名?值不值得考
网上起电工证可靠吗?学历不高怕报不上名?值不值得考

网上起电工证可靠吗?学历不高怕报不上名?值不值得考 你是不是也这样想, 学历不高怕报不上名 ?想进工地、进厂子、做维修,总听说要先有电工证,但又不知道网上起的电工证靠不靠谱?别急,下面我就从 开封初次报考人员… · 2026/11/15 12:11:20

电工证能考多久与全国通用特种作业证的报考指南
电工证能考多久与全国通用特种作业证的报考指南

电工证能考多久与全国通用特种作业证的报考指南 急需特种作业证才能上岗找工作,但你有没有想过,电工证到底能考多久?全国通用的特种作业证是否真的在所有地方都适用?作为许昌劳务班组的负责人,我深知电工证在找工作时的重要性,也了解很多新手在报考过程… · 2026/11/15 12:11:20

附近考建筑电工证机构怎么选 7天拿证别被坑
附近考建筑电工证机构怎么选 7天拿证别被坑

附近考建筑电工证机构怎么选 7天拿证别被坑 怕考不过白交培训费?附近考建筑电工证机构到底怎么选?你不是一个人。很多在南阳从事水利工程的电工师傅,为了拿到低压电工证或高压电工证,跑遍了周边的培训机构,结果要么培训内容不全,要么考试通过率低,花… · 2026/11/15 12:11:20

玉树市电工证培训哪里靠谱?电子证书怎么考?
玉树市电工证培训哪里靠谱?电子证书怎么考?

玉树市电工证培训哪里靠谱?电子证书怎么考? 不知道去哪报名怕被中介坑,玉树市电工证培训越来越多人关注,但信息真假混杂,一不小心就踩坑。特别是对刚毕业的工科学生来说,找工作、进工地、入职都需要电工证,但 电子证书… · 2026/11/15 12:10:19

长春初级电工证怎么考?通过率揭秘全在这里
长春初级电工证怎么考?通过率揭秘全在这里

长春初级电工证怎么考?通过率揭秘全在这里 之前考的证在外省能不能转过来?这几乎是每个想从事电工工作的人都会问的问题。特别是那些在外地打拼多年、手里握着一张“外地电工证”的人,到了长春想继续干这一行,第一反应就是“我的证能用吗?”。今天我们就… · 2026/11/15 0:00:50

国内电工证报考条件与考试时间
国内电工证报考条件与考试时间

低压电工证怎么考?学历不高也能报名,官方报名入口全在这 你是不是经常听说“电工证是入行门槛”,但又担心自己学历不够、经验不足,根本报不了名?别急,今天就带你从零了解【国内电工证】的报考流程、考试内容,还有怎么在 官方报名入口… · 2026/11/15 0:00:50

电工作业时如何断电?电子证书到底能不能全国通用?
电工作业时如何断电?电子证书到底能不能全国通用?

电工作业时如何断电?电子证书到底能不能全国通用? 实操考试心里没底怕挂科,特别是电工作业时如何断电这一操作,不仅关系到你能不能拿到证书,更关系到以后能不能顺利上岗。很多人觉得这不过就是“断个电”,但其实这里面的门道可不少,稍有不慎就可能出错… · 2026/11/15 0:00:50

长春初级电工证怎么考?通过率揭秘全在这里
长春初级电工证怎么考?通过率揭秘全在这里

长春初级电工证怎么考?通过率揭秘全在这里 之前考的证在外省能不能转过来?这几乎是每个想从事电工工作的人都会问的问题。特别是那些在外地打拼多年、手里握着一张“外地电工证”的人,到了长春想继续干这一行,第一反应就是“我的证能用吗?”。今天我们就… · 2026/11/15 0:00:50

国内电工证报考条件与考试时间
国内电工证报考条件与考试时间

低压电工证怎么考?学历不高也能报名,官方报名入口全在这 你是不是经常听说“电工证是入行门槛”,但又担心自己学历不够、经验不足,根本报不了名?别急,今天就带你从零了解【国内电工证】的报考流程、考试内容,还有怎么在 官方报名入口… · 2026/11/15 0:00:50

电工作业时如何断电?电子证书到底能不能全国通用?
电工作业时如何断电?电子证书到底能不能全国通用?

电工作业时如何断电?电子证书到底能不能全国通用? 实操考试心里没底怕挂科,特别是电工作业时如何断电这一操作,不仅关系到你能不能拿到证书,更关系到以后能不能顺利上岗。很多人觉得这不过就是“断个电”,但其实这里面的门道可不少,稍有不慎就可能出错… · 2026/11/15 0:00:50

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/15 1:51:20

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/15 4:02:19

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/15 3:02:05

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码