Java 8 introduces LongAdder
, LongAccumulator
, DoubleAdder
and DoubleAccumulator
, which are recommended instead of the Atomic
classes when multiple threads update frequently but read less frequently (for example, in the context of gathering statistics). Under high thread contention, these new classes are designed to grow dynamically and, therefore, there is higher throughput at the expense of higher space consumption.
The "Adder" classes support operations for additions, whereas the "Accumulator" clases are given a function to combine values.
To understand how these classes work, you need to take a look at the implementation of the base class, Striped64
. It has a "base" field, plus a table of "cells", each of which is a padded variant of AtomicLong
to reduce cache contention. It first tries to update the base field using the usual AtomicLong
CAS operation. If there is no contention, CAS will succeed. However, if there is contention (i.e. the CAS update of "base" failed), it will use the "probe" value (hash value) of the thread to map to a cell in the table. If a cell does not exist, it will be created. New cells are created upon contention until the number of cells reaches the nearest power of two greater than or equal to the number of CPUs. If the cell exists, CAS will be tried to update the value of the cell. If CAS fails, the probe value will be updated using a secondary hash until an uncontended cell is found. Finally, the sum
method simply adds the base field and the elements in the cells array.
The code below shows how you can use LongAdder
to calculate the sum of several values:
LongAdder adder = new LongAdder(); // do some addition in different threads adder.add(10); adder.increment(); // ... // get the current sum long sum = adder.sum();
Or you can use LongAccumulator
as follows:
LongAccumulator acc = new LongAccumulator(Long::sum, 0); acc.accumulate(10); adder.increment(); // ... // get the current sum long sum = acc.get();
You can also use LongAdder
in conjuction with a ConcurrentHashMap
to maintain a frequency map:
ConcurrentMap<String, LongAdder> freqs = new ConcurrentHashMap<>(); freqs.computeIfAbsent("foo", k -> new LongAdder()).increment();