LongAdder的表现如何比AtomicLong更好

我看到Java的AtomicInteger如何在内部使用CAS(比较和交换)操作。 基本上,当多个线程尝试更新该值时,JVM在内部使用底层CAS机制并尝试更新该值。 如果更新失败,则再次尝试使用新值但从不阻止。

在Java8中,Oracle引入了一个新的类LongAdder ,它在高争用下似乎比AtomicInteger表现更好。 一些博客文章声称LongAdder通过维护内部单元格表现更好 – 这是否意味着LongAdder在内部聚合值并在以后更新它? 你能帮我理解LongAdder的工作原理吗?

这是否意味着LongAdder在内部聚合值并在以后更新?

是的,如果我理解你的陈述。

LongAdder中的每个Cell都是AtomicLong的变体。 具有多个这样的小区是扩展争用并因此增加吞吐量的一种方式。

当要检索最终结果(总和)时,它只是将每个单元格的值加在一起。

关于如何组织单元格,如何分配单元格等的大部分逻辑可以在源代码中看到: http : //hg.openjdk.java.net/jdk9/jdk9/jdk/file/f398670f3da7/src/java.base /share/classes/java/util/concurrent/atomic/Striped64.java

特别是单元的数量受CPU数量的限制:

 /** Number of CPUS, to place bound on table size */ static final int NCPU = Runtime.getRuntime().availableProcessors(); 

“更快”的主要原因是它的竞争性能 。 这很重要,因为:

在低更新争用下,这两个类具有相似的特征。

您将使用LongAdder进行非常频繁的更新,其中primefacesCAS和对Unsafe本机调用将导致争用 。 (参见source和volatile读取 )。 更不用说多个AtomicLongs上的缓存未命中/错误共享 (虽然我还没有看过类布局,但在实际的long字段之前似乎没有足够的内存填充。

在高争用的情况下,这一类的预期吞吐量明显更高,但代价是空间消耗更高。

实现扩展了Striped64 ,它是64位值的数据持有者。 这些值保存在单元格中,这些单元格用于填充(或条带化),因此也就是名称。 每个操作都可以在LongAdder上修改Striped64中存在的值集合。 当发生争用时,会创建并修改新的单元格,因此旧的线程可以与争用的单元格同时完成。 当您需要最终值时,只需添加每个单元格的总和。

不幸的是,性能带来了成本,在这种情况下是内存(通常是)。 如果向它抛出大量线程和更新,Striped64可能会变得非常大。

引用来源: LongAdder的Javadoc