atomiclong高并发变慢的根本原因是mesi协议引发的缓存行乒乓效应:多核争抢同一缓存行导致频繁失效与总线竞争,使cas读-比-写操作失败率飙升。

AtomicLong 的原子递增在高并发下变慢,根本原因不是代码写得不好,而是现代多核 CPU 的缓存一致性机制在“拖后腿”。
单点更新撞上 MESI 协议
AtomicLong 内部只维护一个 volatile long value。每次 incrementAndGet() 都靠 CAS 操作去争抢更新它。所有线程无论在哪一个 CPU 核上运行,最终都要修改同一块内存地址——这个地址大概率落在同一个 64 字节的缓存行里。
一旦多个核心同时写这个缓存行,CPU 就必须通过 MESI(Modified-Exclusive-Shared-Invalid)协议协调:
- CPU1 修改 value → 把该缓存行标记为 Modified,并通知其他核心把对应缓存行置为 Invalid
- CPU2 想更新 → 发现自己缓存里的那一行已失效 → 只能重新从主存或其它核心拉取整行数据 → 等待总线响应
- 反复如此,就形成了“缓存行乒乓”(Cache Line Ping-Pong)
总线竞争让 CAS 失败率飙升
CAS 本质是“读-比-写”三步原子操作。但在缓存行频繁失效的情况下:
- 线程刚读到旧值,还没来得及比较,缓存行就被别的核刷掉了
- 更多线程卡在重试循环里,进一步加剧总线请求压力
这不是算法问题,是硬件层面的资源争抢:所有线程都在排队等同一条总线通道,就像早高峰地铁闸机只有一台,人越多反而走得越慢。
伪共享让问题雪上加霜
如果两个 AtomicLong 实例在内存中挨得很近(比如定义在同一个对象里),它们很可能被加载进同一个缓存行:
- 线程 A 改 counter1 → 整个缓存行失效
- 线程 B 此刻要改 counter2 → 虽然变量不同,但必须等缓存行重新加载才能操作
- 逻辑无关的操作,物理上却互相阻塞
这种“伪共享”(False Sharing)会让 AtomicLong 的实际性能比理论值更低,尤其在密集更新多个计数器的场景中特别明显。
为什么 LongAdder 能绕开这个问题
它不硬扛单点竞争,而是主动拆解:
- 用 base 做低并发兜底,用 cells 数组分摊高并发压力
- 每个 Cell 占用独立缓存行(@Contended 字节填充防伪共享)
- 线程按 probe 哈希分散写入不同 Cell,彼此不再争同一行
- 写操作几乎不触发跨核缓存同步,总线压力大幅下降
不是 AtomicLong 不够快,而是它没为“多核并行写同一地址”这个典型场景做适配。LongAdder 才是专治这类瓶颈的对症药。











