连载中 8/21

原子类全家桶:从 AtomicInteger 到 LongAdder 的分摊艺术

2026-10-26 · 45 阅读 · 0 评论 · 0 赞

先认全家族

java.util.concurrent.atomic 包比想象的大,按用途分五类:基本类型——AtomicInteger / AtomicLong / AtomicBoolean,计数与状态位;引用类型——AtomicReference(对象引用整体替换)、AtomicStampedReference(带版本号治 ABA)、AtomicMarkableReference(布尔标记版);数组——AtomicIntegerArray 等,数组元素的原子读写;字段更新器——AtomicIntegerFieldUpdater 等,给已有对象的 volatile 字段补上 CAS 能力,不用为个别字段重建整个类;累加器——LongAdder / DoubleAdder / LongAccumulator,高并发统计专用,本篇主角。

AtomicLong 的痛点:一个热点吵翻全场

几十个线程并发调 AtomicLong.incrementAndGet 时,CAS 失败率飙升:每次自增都要和全体线程抢同一个内存地址,失败、重试、再失败——上一篇写的那个 do-while 循环,在高并发下变成了自旋内耗,吞吐量随线程数不升反降。热点在哪?所有线程挤在同一个 value 上。

LongAdder:打不过就分家

LongAdder 的思路一句话:把热点摊开。它内部维护一个 base 加一个 Cell 数组:无竞争时只对 base 做 CAS,和 AtomicLong 一样快;一旦发现竞争(base CAS 失败),失败的线程被引导到各自的 Cell 槽位上自增——各改各的槽位,几乎零冲突;Cell 数组还会按竞争程度自动扩容。取值时 sum() 把 base 和所有 Cell 求和:

LongAdder adder = new LongAdder();
adder.increment();   // 各线程自增,内部自动分摊到 Cell
adder.sum();         // 需要时汇总:base + 所有 Cell 之和

代价也直说:sum() 不是瞬时精确值——汇总时别的线程可能还在改,拿到的是弱一致的近似值。于是选型一句话:要精确的原子计数语义(生成序号、精确配额扣减)用 AtomicLong;要高并发统计(QPS、请求数、调用次数)用 LongAdder。统计场景在乎的是量级正确,不是某一瞬间的精确快照。

LongAccumulator:自定义累积运算

LongAdder 只会加法。LongAccumulator 允许传入自定义累积函数(求最大值、最小值、任意满足结合律的运算),内部同样是分摊思想——LongAdder 本质上就是它取「加法」的特例。需要滑动最大值这类统计时,直接用它,不必自己造轮子。

无锁化的武器库到此齐备。但业务里大量场景是「一段逻辑整体不能被打断」——单变量 CAS 管不住,synchronized 又想要更灵活的中断、超时、公平选项。下一篇进入整个 JUC 的地基工程:AQS,看一把显式锁是怎么从零造出来的。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#Java#原子类#LongAdder#LongAccumulator#JUC

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10113 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9876 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9306 阅读 · 21 评论 · 287 赞