LongAdder的性能如何优于AtomicLong

2022-09-01 16:05:10

我看到Java的AtomicInteger如何在内部与CAS(Compare And Swap)操作一起工作。基本上,当多个线程尝试更新值时,JVM 在内部使用底层 CAS 机制并尝试更新值。如果更新失败,请使用新值重试,但永远不会阻止。

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


答案 1

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

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

中的每个都是 的变体。拥有多个这样的细胞是分散争用的一种方式,从而提高通量。CellLongAdderAtomicLong

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

关于细胞如何组织,如何分配它们等的许多逻辑都可以在源中看到: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();

答案 2

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

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

您将使用 LongAdder 进行非常频繁的更新,其中原子 CAS 和对 Unsafe 的本机调用会导致争用。(请参阅读取和易失性读取)。更不用说多个AtomicLongs上的缓存未命中/错误共享(尽管我还没有查看类布局,但在实际字段之前似乎没有足够的内存填充。long

在高争用下,此类的预期吞吐量明显较高,但代价是空间消耗较高。

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

不幸的是,性能是有代价的,在这种情况下,这是内存(通常就是这样)。如果向 Striped64 抛出大量线程和更新负载,则 Striped64 可能会变得非常大。

引用来源:Javadoc for LongAdder


推荐