【问题标题】:java 8 : Are LongAdder and LongAccumulator preferred to AtomicLong?java 8:LongAdder 和 LongAccumulator 是否优于 AtomicLong?
【发布时间】:2016-06-11 20:40:04
【问题描述】:

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

LongAccumulatorLongAdder 的更通用版本

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?

【问题讨论】:

    标签: java concurrency java-8


    【解决方案1】:

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

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

    来自LongAccumulator

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

    [...]

    LongAdder 类为维护计数和总和的常见特殊情况提供此类功能的类似物。调用new LongAdder() 等效于new LongAccumulator((x, y) -> x + y, 0L)

    因此,使用其中一个取决于您的应用程序打算做什么。它并不总是严格优选的,只有在预期高并发并且您需要保持公共状态时。

    【讨论】:

    • 现在有所不同了。你能解释一下上面代码 sn-p 中的二元运算符部分吗?
    • @ravindra LongBinaryOperator?这是一个功能接口,它定义了一个以 2 longs 作为参数并返回 long 的功能方法。因此,它可用于对 2 个 long 进行操作并从某个计算中返回一个 long。例如,将 2 个 long 相加:LongBinaryOperator longAdder = (x, y) -> x + y;
    【解决方案2】:

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

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

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

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

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

    因此,Cell 的数量可能会过多。如果有人对此有更多详细信息,我想听听你的意见。

    【讨论】:

    • 您的答案似乎是错误的问题。
    • @ravindra 怎么样?问题是关于选择的,我回答了这个问题。
    • 知道了。您能否详细说明 Probe 以及与这三个类的关系?
    • 每个线程没有一个单元格——这更容易实现。实际单元的数量取决于争用,换句话说,它的最大值与 CPU 内核的数量有关。如果你的线程比核心多得多,它们就不能全部争夺单元,因为它们不能同时真正运行。但是我们在谈论什么数字?与每个线程自身的开销相比,每个线程的单元格(即封装的 long 值)可以忽略不计。
    • 关于 Probe 的代码中的 cmets 并不多。如果 Probe 与 CPU 内核相关(如@Holger 所说),那么 LongAdder 等可能是比 AtomicLong 更好的选择。
    猜你喜欢
    • 2015-08-21
    • 1970-01-01
    • 2017-12-29
    • 2021-05-29
    • 1970-01-01
    • 1970-01-01
    • 2016-08-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多