LongAdder通过分段累加(base+cells数组)分散CAS竞争,避免AtomicInteger单变量热点瓶颈,高并发下吞吐量提升5–10倍,适用于QPS统计等写密集场景。

CAS 配合 Unsafe.compareAndSwapInt 实现高性能计数器,核心在于用无锁、原子的“比较-交换”替代传统加锁,避免线程阻塞和上下文切换开销。关键不是单次 CAS 要多快,而是整个结构如何减少竞争、分散压力、降低自旋失败率。
底层靠 CPU 指令保证原子性
compareAndSwapInt 是 sun.misc.Unsafe 提供的 native 方法,最终调用 x86 的 cmpxchg 汇编指令。它在一个 CPU 周期内完成“读取内存值 → 比较是否等于预期值 → 相等则写入新值”三步,不可中断。这意味着无需进入操作系统内核、不触发线程调度,天然轻量。
直接用 Unsafe 写基础计数器要小心重试逻辑
单纯封装一个 int 字段 + CAS 循环,容易陷入高竞争下的长时自旋。典型写法如下:
public class SimpleCounter {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
private volatile int value;
static {
try {
valueOffset = unsafe.objectFieldOffset(
SimpleCounter.class.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
public void increment() {
int current;
do {
current = unsafe.getIntVolatile(this, valueOffset);
} while (!unsafe.compareAndSwapInt(this, valueOffset, current, current + 1));
}
public int get() {
return unsafe.getIntVolatile(this, valueOffset);
}
}
这个实现在低并发下没问题,但高并发时多个线程反复读-比-换失败,会空转消耗 CPU,且所有线程争同一个内存地址,成为瓶颈。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
立即学习“Java免费学习笔记(深入)”;
LongAdder 的分段累加才是高性能的关键
Java 8 引入的 LongAdder 正是为解决上述问题:它把一个计数器拆成多个槽(cells),线程先尝试更新自己的槽;槽冲突时再扩容或退回到 base 字段。最终求和只需遍历所有非空槽加 base,不需原子操作。
- 每个线程通过
ThreadLocalRandom或 probe 值定位专属 cell,大幅降低 CAS 冲突概率 - cell 数组采用懒加载 + CAS 扩容,避免初始化开销
- 当竞争不激烈时,所有更新都落在 base 上,行为退化为 AtomicLong;竞争加剧后自动分片
- sum() 操作是非实时的(弱一致性),但对监控、统计类场景完全够用,且无锁开销
为什么不用 AtomicInteger 就行?
AtomicInteger 本质就是对一个 int 字段做 CAS,适合读多写少、更新频次不高的场景。而计数器类需求(如 QPS 统计、请求总数)往往写密集、并发极高,此时:
- AtomicInteger 的单一变量成为热点,CAS 失败率飙升,CPU 自旋浪费严重
- LongAdder 通过空间换时间,用数组分散写压力,实测在 4 核以上机器上吞吐量可高出 5–10 倍
- 它的设计目标明确:不是强一致计数器,而是高吞吐、低延迟的累加器


















