比较和交换和阻塞算法之间的性能比较

时间:2015-02-20 14:40:33

标签: java concurrency blocking lock-free

我有ConcurrentLinkedQueue我用作基础数据结构。在每次看涨期权中,我都会向列表中添加一个唯一的递增值。我有这个方法的synchronized和compare-and-swap版本。当我有几个线程(例如,5个)并完成1000万次放入时,我看到同步版本的效果要好得多。当我有很多线程(例如,2000)并且总共执行相同数量的放置时,我看到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;
            }
        }
    }

统计

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

1 个答案:

答案 0 :(得分:3)

TL; DR ,因为在非竞争情况下,JVM将优化synchronized并将其替换为CAS锁。

在您的CAS案例中,您有开销:即使您的CAS失败,您也会尝试进行一些计算。当然,与真正的互斥量获取相比,它没什么用,当您使用synchronized时通常会发生这种情况。

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

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