Java 比较、交换和阻塞算法的性能比较

Java 比较、交换和阻塞算法的性能比较,java,concurrency,blocking,lock-free,Java,Concurrency,Blocking,Lock Free,我有一个ConcurrentLinkedQueue,用作底层数据结构。每次put调用时,我都会向列表中添加一个唯一的递增值。我有这个方法的synchronized和compare-and-swap版本。当我只有很少的线程(例如,5个)并且总共做了1000万次put时,我发现同步版本工作得更好。当我有多个线程(例如,2000个)并且总共执行相同数量的put时,我发现CAS工作得更好。为什么CAS的性能不如线程较少的阻塞算法 // AtomicReference<Foo> latestV

我有一个
ConcurrentLinkedQueue
,用作底层数据结构。每次put调用时,我都会向列表中添加一个唯一的递增值。我有这个方法的synchronized和compare-and-swap版本。当我只有很少的线程(例如,5个)并且总共做了1000万次put时,我发现同步版本工作得更好。当我有多个线程(例如,2000个)并且总共执行相同数量的put时,我发现CAS工作得更好。为什么CAS的性能不如线程较少的阻塞算法

// AtomicReference<Foo> latestValue that is initialized
    public void put(Double value) {
        Foo currentValue;
        while (true) {
            currentValue = latestValue.get();
            Foo newValue = new Foo(value);
            if (latestValue.compareAndSet(currentValue, newValue)) {
                historyList.add(newValue);
                return;
            }
        }
    }

TL;DR因为在无竞争的情况下,JVM将优化
synchronized
,并用CAS锁替换它

在您的CAS案例中,您得到了开销:您正在尝试进行一些计算,即使您的CAS将失败。当然,与真正的互斥体获取相比,这算不了什么,当您使用
synchronized
时通常会发生什么

但是JVM并不愚蠢,当它看到您当前获取的锁是无约束的时,它只是用CAS锁(或者在有偏差的锁定情况下,甚至用简单的存储)替换真正的互斥锁。 因此,对于
synchronized
中的两个线程,您只测量了一个CAS,但对于您自己的CAS实现,您还测量了分配Foo实例的时间,例如compareAndSet和get()


对于2000个线程,JVM不执行CAS优化,因此您的实现比预期的互斥获取更出色。

我认为您的观察结果是关键。@Fildor您能详细说明一下吗?如果涉及更多线程,那么无锁工作就更重要了。因为碰撞的概率会高得多。如果您只有几个线程,那么开销较小的实现可能会执行得更好,即使它包含锁定。@Fildor好的,我理解这一部分。我不明白的是,为什么当线程数较少时,CAS的性能不好。您对“坏”的定义是什么?您是如何实现CAS的?也许还有一些优化的空间?我已经为你的案例编辑了我的答案,以更好地解释它。关于JVM CAS优化,你有什么来源可以指向我吗?@user592748当然。这是官方文件,你可以在那里找到布赖恩·戈茨的一些评论。如果您对特定的实现感兴趣,可以在这里找到一些信息:在这里可以找到实际的解释:src/cpu/x86/vm/template解释器或在hotspot/src/share/vm/解释器中/
NON-BLOCKING
Threads 2000
Puts per thread 10000
Put time average    208493309

BLOCKING
Threads 2000
Puts per thread 10000
Put time average    2370823534


NON-BLOCKING
Threads 2
Puts per thread 10000000
Put time average    13117487385

BLOCKING
Threads 2
Puts per thread 10000000
Put time average    4201127857