LongAccumulator的核心价值在于支持可编程累加语义,通过LongBinaryOperator定义规则,实现最大值、连乘积、加权和等灵活运算,并依托Striped64分段机制保障高并发写性能。

LongAccumulator 的核心价值,不在于“替代 AtomicLong”,而在于把“累加”这件事从固定语义(+1、+n)升级为可编程语义——你定义规则,它保障并发安全。
函数式接口是规则入口
构造 LongAccumulator 时必须传入 LongBinaryOperator 和初始值:new LongAccumulator((left, right) -> left + right, 0)
这个 lambda 就是计算规则:给定当前累积值(left)和新输入值(right),返回下一个累积值。
它不是“执行一次加法”,而是“定义整个累加代数系统”。因此你可以轻松实现:
- 取最大值:
(a, b) -> Math.max(a, b),初始值设为Long.MIN_VALUE - 连乘积:
(a, b) -> a * b,初始值必须为1(否则结果恒为 0) - 带权重的加权和:
(sum, item) -> sum + item.value * item.weight,前提是传入前已提取好数值 - 位或聚合:
(a, b) -> a | b,适合状态标志合并
线程本地累积 + 批量提交更高效
直接对每个原始对象调用 accumulate(order) 并在 lambda 里取字段,会引入额外对象引用和方法调用开销。高并发下应避免。
推荐做法是:线程内先做轻量预处理,再批量提交数值。
- ❌ 不推荐:
acc.accumulate(order)(lambda 中再写order.getAmount()) - ✅ 推荐:
long batchSum = orders.stream().mapToLong(Order::getAmount).sum(); acc.accumulate(batchSum); - ✅ 更优(无流开销):
long batchSum = 0; for (Order o : orders) batchSum += o.getAmount(); acc.accumulate(batchSum);
这减少了热点路径上的函数调用次数和内存访问跳转,实测在百万级批处理中可降低 15%–25% 累加耗时。
读操作有成本,别滥用 sum()
sum() 方法需遍历所有 Cell 并合并 base 值,在高并发场景下可能触发缓存行争用,且不是原子快照(中间可能被其他线程修改)。
若需“读完即清零”的原子语义,唯一正确方式是 sumThenReset() —— 它以 CAS 保证读与重置的原子性。但注意:该操作会重置内部状态,后续 accumulate 从 identity 重新开始,不适合持续累加场景。
- 监控
sum()调用频次,尤其避免在 HTTP 请求响应链路中高频调用 - 统计类服务可每秒/每分钟定时调用一次
sumThenReset(),作为指标上报 - 如需近实时值又不想阻塞,可考虑用 volatile long 缓存最近一次
sum()结果,配合后台线程定期刷新
底层仍是 Striped64,理解分段逻辑才能用好
LongAccumulator 继承自 Striped64,其高性能本质是“分段竞争”:
- 低并发时:所有线程竞争
base字段,类似 AtomicLong - 高并发时:自动扩容
Cell[]数组,线程通过哈希映射到不同 Cell,各自 CAS 自己的槽位 - 最终
sum()是 base + 所有非空 Cell.value 的总和
这意味着:它的优势只在写多读少、且写操作密集的场景中充分显现;若频繁读+极少写,AtomicLong 反而更轻量。
不复杂但容易忽略:它的强大来自函数抽象与并发控制的结合,而不是魔法。写对规则,用对时机,才是关键。

















