【问题标题】:How is LongAccumulator implemented, so that it is more efficient?LongAccumulator 是如何实现的,这样效率更高?
【发布时间】:2014-05-23 17:15:05
【问题描述】:

我了解到新的Java(8)引入了新的同步工具如LongAccumulator(在atomic包下)。

在文档中说,当来自多个线程的变量更新频繁时,LongAccumulator 效率更高。

我想知道它是如何实现更高效的?

【问题讨论】:

    标签: java concurrency atomic java-8


    【解决方案1】:

    这是一个非常好的问题,因为它展示了使用共享内存进行并发编程的一个非常重要的特征。在详细介绍之前,我必须退后一步。看看下面的类:

    class Accumulator {
        private final AtomicLong value = new AtomicLong(0);
        public void accumulate(long value) {
            this.value.addAndGet(value);
        }
        public long get() {
            return this.value.get();
        }
    }
    

    如果你创建这个类的一个实例并在一个循环中从一个线程调用方法accumulate(1),那么执行会非常快。但是,如果您从 两个线程 对同一实例调用该方法,则执行速度将慢大约 两个数量级

    您必须查看内存架构才能了解会发生什么。现在大多数系统都有non-uniform memory access。特别是,每个内核都有自己的 L1 高速缓存,它通常被结构化为具有 64 个八位字节的高速缓存行。如果一个核心对内存位置执行原子增量操作,它首先必须获得对相应缓存行的独占访问权限。如果它还没有独占访问权限,那是很昂贵的,因为需要与所有其他内核进行协调。

    有一个简单且违反直觉的技巧可以解决这个问题。看看下面的类:

    class Accumulator {
        private final AtomicLong[] values = {
            new AtomicLong(0),
            new AtomicLong(0),
            new AtomicLong(0),
            new AtomicLong(0),
        };
        public void accumulate(long value) {
            int index = getMagicValue();
            this.values[index % values.length].addAndGet(value);
        }
        public long get() {
            long result = 0;
            for (AtomicLong value : values) {
                result += value.get();
            }
            return result;
        }
    }
    

    乍一看,由于额外的操作,这个类似乎更昂贵。但是,它可能比第一类快几倍,因为它执行内核已经独占访问所需缓存行的可能性更高。

    要真正快速完成,您必须考虑更多事项:

    • 不同的原子计数器应该位于不同的缓存行上。否则,您将一个问题替换为另一个问题,即false sharing。在 Java 中,您可以为此使用 long[8 * 4],并且只使用索引 081624
    • 必须明智地选择计数器的数量。如果不同的计数器太少,缓存切换仍然太多。如果计数器过多,则会浪费 L1 缓存中的空间。
    • getMagicValue 方法应该返回一个与核心 id 有关联的值。

    综上所述,LongAccumulator对于某些用例来说效率更高,因为它使用冗余内存进行频繁使用的写操作,为了减少必须交换缓存行的次数核心之间。另一方面,读取操作的成本稍高一些,因为它们必须创建一致的结果。

    【讨论】:

    • 这很有趣。谢谢。
    【解决方案2】:
    猜你喜欢
    • 1970-01-01
    • 2013-06-14
    • 2014-03-13
    • 1970-01-01
    • 2010-12-09
    • 1970-01-01
    • 2013-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多