Java 8 introduces
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();