What does LongAccumulator add over LongAdder, and when would you reach for it?
answer
- LongAccumulator = LongAdder + a custom LongBinaryOperator + identity
- Operator MUST be associative & side-effect-free (cells fold in unspecified order)
- max/min/product/bitwise = safe; subtract/divide = unsafe
- LongAdder is the addition special case
- get() still not an atomic snapshot; getThenReset for intervals
basics
~20 sLongAccumulator is the general version of LongAdder. You give it a function (like max or multiply) and a starting value, and it combines all the values you feed in using that function across multiple cells. LongAdder is just LongAccumulator with addition.
solid answer
~50 sLongAdder is hardwired to addition. LongAccumulator generalizes it: you construct it with a LongBinaryOperator and an identity value, and accumulate() combines the running value with each new one using that operator — so you can compute a running max, min, product, or bitwise-or across many threads with the same striped, low-contention design. It uses the same internal base-plus-cells striping as LongAdder, so it scales the same way under write contention. The crucial constraint is that the operator must be associative and side-effect-free, because values land in different cells in nondeterministic order and get folded together in an unspecified order by get(). Addition and max are associative, so they're safe; subtraction or division are not and would give wrong results. As with LongAdder, get() is not an atomic snapshot. Use LongAccumulator when you need a contended running reduction other than a plain sum.
code
java · 14 linesimport java.util.concurrent.atomic.LongAccumulator;
// Running max latency across many worker threads
LongAccumulator maxLatency =
new LongAccumulator(Long::max, Long.MIN_VALUE);
maxLatency.accumulate(42); // fold 42 in via max, striped across cells
maxLatency.accumulate(17);
maxLatency.accumulate(91);
long worst = maxLatency.get(); // 91 (folded over base + cells)
// LongAdder is just this with addition:
// new LongAccumulator(Long::sum, 0L)go deeper
Aware that LongAccumulator exists and lets you combine values with a function other than plain addition; LongAdder is the add-only version.
Can construct one with an operator and identity (e.g. Long::max, MIN_VALUE) and knows it shares LongAdder's striping and non-snapshot read.
Explains the associativity/side-effect-free requirement and why it follows from cells being folded in unspecified order; picks safe vs unsafe operators correctly and chooses identity properly.
Weighs LongAccumulator against alternatives (AtomicLong.accumulateAndGet at low contention, dedicated metrics histograms), reasons about floating-point associativity caveats with DoubleAccumulator, and the cost/benefit of striped reductions at scale.
## From LongAdder to LongAccumulator `LongAdder` solves one specific problem: a high-contention **sum**. `LongAccumulator` is its **generalization** — same striping machinery, but you supply the combining function instead of it being fixed to `+`. You build one with two things: ```java LongAccumulator max = new LongAccumulator(Long::max, Long.MIN_VALUE); ``` - a **`LongBinaryOperator`** — a function `(currentValue, newValue) -> result`. Here `Long::max`. - an **identity** — the starting/neutral value such that `op(identity, x) == x`. For max it's `Long.MIN_VALUE`; for sum it'd be `0`; for product `1`. `accumulate(x)` folds `x` into the running value using the operator (striped across cells just like LongAdder). `get()` reduces every cell plus the base with the operator to produce the result. `LongAdder` is literally the special case where the operator is addition and the identity is 0. ## The associativity requirement (the part interviewers probe) Because values are distributed across **multiple cells in nondeterministic order**, and `get()` folds those cells together in an **unspecified order**, the combining function **must be associative** — `op(op(a,b),c)` must equal `op(a,op(b,c))` — and ideally **commutative**, and **side-effect-free**. Otherwise the result depends on the order updates happened to land in cells, which is not under your control. - ✅ **Safe**: addition, multiplication, `max`, `min`, bitwise `&`/`|`/`^`. These are associative; order doesn't matter. - ❌ **Unsafe**: subtraction, division, "last writer wins". `(a - b) - c != a - (b - c)`, so you'd get garbage. Don't put side effects (logging, mutation) in the operator either — it may be called any number of times, including during retries. ## Same trade-offs as LongAdder `get()` is **not an atomic snapshot** under concurrent `accumulate()` — it's eventually-consistent, fine for monitoring/statistics. `getThenReset()` resets to the identity for interval-style reductions. Memory cost is the same striped-cell array. The fast path is lock-free CAS; it only locks briefly to allocate/resize cells. ## When to choose it Reach for `LongAccumulator` when you need a **contended running reduction that isn't a plain sum** — e.g. the maximum latency observed across all worker threads, the minimum free-buffer count, a running bitwise-OR of feature flags seen. If you only need a sum, use `LongAdder` (clearer intent, no operator to get wrong). If contention is low, a single `AtomicLong` with an `accumulateAndGet`/`updateAndGet` loop may be simpler. There are also `DoubleAdder`/`DoubleAccumulator` for floating-point, with the extra caveat that floating-point addition isn't perfectly associative, so results can differ slightly run-to-run.
- Why must the accumulator function be associative?Updates are striped across cells in nondeterministic order, and get() folds the cells together in an unspecified order. If the operator isn't associative (e.g. subtraction), the final result depends on that order, which you don't control — so it would be nondeterministic and wrong.
- How would you express a max counter with LongAdder?You can't — LongAdder only does addition. Use LongAccumulator(Long::max, Long.MIN_VALUE). LongAdder is exactly LongAccumulator with the + operator and identity 0.
saying these in an interview costs you the question
- Using a non-associative operator like subtraction or division
- Putting side effects in the operator (it may be invoked multiple times)
- Thinking LongAccumulator gives a precise atomic snapshot from get()
- Believing it's slower/different in scaling from LongAdder — it uses the same striping
- Forgetting to pass an identity that's neutral for the operator (e.g. 0 for max breaks negative inputs)