简体   繁体   中英

java 8 : Are LongAdder and LongAccumulator preferred to AtomicLong?

LongAdder as an alternative to AtomicLong

ExecutorService executor = Executors.newFixedThreadPool(2);    
IntStream.range(0, 1000)
    .forEach(i -> executor.submit(adder::increment));    
stop(executor);    
System.out.println(adder.sumThenReset());   // => 1000

LongAccumulator is a more generalized version of LongAdder

LongBinaryOperator op = (x, y) -> 2 * x + y;
LongAccumulator accumulator = new LongAccumulator(op, 1L);

ExecutorService executor = Executors.newFixedThreadPool(2);
IntStream.range(0, 10)
    .forEach(i -> executor.submit(() -> accumulator.accumulate(i)));
stop(executor);

System.out.println(accumulator.getThenReset());     // => 2539

I have some queries.

  1. Is LongAdder always preferred to AtomicLong?
  2. Is LongAccumulator preferred to both LongAdder and AtomicLong?

The difference between those classes, and when to use one over the other, is mentioned in the Javadoc. From LongAdder :

This class is usually preferable to AtomicLong when multiple threads update a common sum that is used for purposes such as collecting statistics, not for fine-grained synchronization control. Under low update contention, the two classes have similar characteristics. But under high contention, expected throughput of this class is significantly higher, at the expense of higher space consumption.

And from LongAccumulator :

This class is usually preferable to AtomicLong when multiple threads update a common value that is used for purposes such as collecting statistics, not for fine-grained synchronization control. Under low update contention, the two classes have similar characteristics. But under high contention, expected throughput of this class is significantly higher, at the expense of higher space consumption.

[...]

Class LongAdder provides analogs of the functionality of this class for the common special case of maintaining counts and sums. The call new LongAdder() is equivalent to new LongAccumulator((x, y) -> x + y, 0L) .

Thus, the use of one over the other depends on what your application intends to do. It is not always strictly prefered, only when high concurrency is expected and you need to maintain a common state.

@Tunaki answered the question skillfully, but there is still one issue that affects the choice.

Adding to a Cell requires a Cell per thread. The internal code uses getProbe() in Striped64 which returns:

return UNSAFE.getInt(Thread.currentThread(), PROBE);

Probe is a system field used in threadLocalRandomSeed.

My understanding is that probe is unique for each thread. If you have a high number of threads create/destroy then probe is created for each new thread.

Therefore, the number of Cells may become excessive. If someone has more detail on this I would like to hear from you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM