java 8:LongAdder和LongAccumulator是AtomicLong的首选吗?

LongAdder作为AtomicLong的替代品

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

LongAccumulatorLongAccumulator的更通用版本

 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 

我有一些疑问。

  1. LongAdder总是优先于AtomicLong吗?
  2. LongAccumulator是LongAdder和AtomicLong的首选吗?

Javadoc中提到了这些类之间的差异,以及何时使用其中一个类。 来自LongAdder

当多个线程更新用于收集统计信息但不用于细粒度同步控制的目的的公共和时,此类通常优于AtomicLong 。 在低更新争用下,这两个类具有相似的特征。 但在高争用的情况下,这一类的预期吞吐量明显更高,但代价是空间消耗更高。

并从LongAccumulator

当多个线程更新用于收集统计信息等目的的公共值时,此类通常优于AtomicLong ,而不是用于细粒度同步控制。 在低更新争用下,这两个类具有相似的特征。 但在高争用的情况下,这一类的预期吞吐量明显更高,但代价是空间消耗更高。

[…]

LongAdder类提供了此类function的类比,用于维护计数和总和的常见特殊情况。 调用new LongAdder()相当于new LongAccumulator((x, y) -> x + y, 0L)

因此,使用一个优先于另一个取决于您的应用程序打算做什么。 它并不总是严格优先,只有当预期高并发并且您需要维持一个共同状态时。

@Tunaki熟练地回答了这个问题,但仍有一个问题影响了选择。

添加到单元格需要每个线程一个单元格。 内部代码使用Striped64中的getProbe()返回:

返回UNSAFE.getInt(Thread.currentThread(),PROBE);

Probe是threadLocalRandomSeed中使用的系统字段。

我的理解是探针对于每个线程都是唯一的。 如果你有大量的线程创建/销毁,那么为每个新线程创建探针。

因此,Cell的数量可能变得过多。 如果有人有更多细节,我想听听你的意见。