longaccumulator支持自定义二元累积操作,需满足结合律、有中性元且为纯函数;如按位与用(a,b)->a&b和identity=-1l,而减法、除法等不满足结合律的操作不可用。

LongAccumulator 在高并发场景下通过 CAS + 分段累加(cell 分片)机制实现高性能原子累加,它本身不直接支持“自定义运算逻辑”的累加(比如非加法),但可以通过构造时传入自定义的 BinaryOperator
理解 LongAccumulator 的设计本质
LongAccumulator 不是“只能加”,而是:给定一个满足结合律、且有中性元(identity)的二元操作 f(a,b),它能在线程安全前提下高效执行 f(f(...f(identity, x1), x2), ..., xn)。常见用法是加法(f = +,identity = 0),但也可以是位或(|)、最小值(Math::min)、最大值(Math::max)等。
关键约束:
- 操作必须是 无副作用、纯函数(不能修改入参,不能依赖外部状态)
- 必须满足 结合律:f(f(a,b),c) == f(a,f(b,c)),否则分段累加结果不可靠
- 必须提供正确的 identity 值:f(identity, x) == x 对所有 x 成立(如加法 identity=0,位或 identity=0,最小值 identity=Long.MAX_VALUE)
如何实现自定义原子累加(以“按位与”为例)
目标:多个线程并发执行 accumulator.accumulate(x),最终结果等于所有 x 的按位与(&)。
代码示例:
LongAccumulator andAccumulator = new LongAccumulator(
(a, b) -> a & b, // 自定义累积函数:取两数按位与
Long.MAX_VALUE // identity:全 1(因为 x & ~0 == x;注意 Long.MAX_VALUE 是 0x7fff...,严格说不是全 1;真正全 1 是 -1L)
);
// 更严谨写法(使用 -1L 作为 identity,因 (-1L & x) == x 恒成立)
LongAccumulator andAccumulatorSafe = new LongAccumulator(
(a, b) -> a & b,
-1L
);
调用方式不变:andAccumulatorSafe.accumulate(0b1011); andAccumulatorSafe.accumulate(0b1101); → 最终 get() 返回 0b1001。
常见可安全使用的自定义操作
以下操作均满足结合律 + 易得 identity,适合 LongAccumulator:
-
加法:
(a,b) -> a+b,identity =0L -
乘法:
(a,b) -> a*b,identity =1L(注意溢出风险) -
位或:
(a,b) -> a|b,identity =0L -
位与:
(a,b) -> a&b,identity =-1L -
最小值:
Math::min,identity =Long.MAX_VALUE -
最大值:
Math::max,identity =Long.MIN_VALUE
⚠️ 注意:Math::max 和 Math::min 是 JDK 内置方法引用,直接传入即可,无需 lambda。
不能用的典型反例
以下操作 不可用于 LongAccumulator,会导致结果错误或未定义行为:
-
减法:
(a,b) -> a-b—— 不满足结合律((a-b)-c ≠ a-(b-c)) -
除法:
(a,b) -> a/b—— 不满足结合律,且 identity 难定义 - 字符串拼接转 long —— 有副作用、非纯函数、非二元 long 操作
- 依赖时间/随机数的操作 —— 违反纯函数要求,结果不可重现
若业务逻辑必须用非结合操作(如顺序敏感的链式处理),LongAccumulator 不适用,应考虑 ConcurrentLinkedQueue + 单线程消费,或 StampedLock 等显式同步方案。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











