java Synchronized vs ReentrantLock 在性能上

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11600520/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 05:44:09  来源:igfitidea点击:

Synchronized vs ReentrantLock on performance

javamultithreading

提问by userx

I have been through a set of few surprises when it comes to Queue implementation for a Multithreading system. Here is:-

在多线程系统的队列实现方面,我经历了一些惊喜。这是:-

The Scenario:- 1 producer, 1 consumer:- A producer puts an integer into a queue. A consumer simply removes it from the queue.

场景:- 1 个生产者,1 个消费者:- 生产者将一个整数放入队列中。消费者只需将其从队列中删除。

The underlying data structure of the queue:- TreeSet (which I never thought I will use), LinkedList, LinkedBlockingQueue(with indefinite size)

队列底层数据结构:- TreeSet(没想到会用)、LinkedList、LinkedBlockingQueue(大小不定)

The code:- of TreeSet as a queue:-

代码:- TreeSet 作为队列:-

while (i < 2000000) {
        synchronized (objQueue) {

            if (!(objQueue.size() > 0)) {
                try {
                    objQueue.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            Integer x = objQueue.first();
            if (x != null) {
                objQueue.remove(x);
                ++i;
            }
        }
    }

EDIT:-

编辑:-

      while (i < 2000000) {
        synchronized (objQueue) {
            objQueue.add(i);
            ++i;
            objQueue.notify();
        }
    }

For LinkedBlockingQueue:-

对于 LinkedBlockingQueue:-

     while (i < 2000000){
        try {
            objQueue.put(i);
            ++i;
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            Thread.currentThread().interrupt();
        }
    }

      while (i < 2000000) {
        try {
            objQueue.take();
            ++i;

        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            Thread.currentThread().interrupt();
        }
    }

For LinkedList :- similar code with synchronized.

对于 LinkedList :- 与同步类似的代码。

The Questions:-

问题:-

1) When I measured the performance via Visual VM, I observed that the for the producer code, TreeSet performs better than LinkedBlockingQueue and LinkedList, even though it takes O(log n) time, the creation of objects in Linked structures is a significant overhead. Why is the theory quite different to the practice ? Why do we prefer Linked, Array structures over Tree structures in queue implementations ?

1) 当我通过 Visual VM 测量性能时,我观察到对于生产者代码,TreeSet 的性能比 LinkedBlockingQueue 和 LinkedList 更好,尽管它需要 O(log n) 时间,但在 Linked 结构中创建对象是一个显着的开销. 为什么理论与实践大相径庭?为什么在队列实现中我们更喜欢链接的数组结构而不是树结构?

2) The synchronized comes out as a clear winner vs the ReeentrantLock because TreeSet performed better than LinkedList which performed better than LinkedBlockingQueue. I wish I could attach the Visual VM results. It is not in votes with the article, http://www.ibm.com/developerworks/java/library/j-jtp10264/index.html

2)synchronized 明显胜出 ReentrantLock,因为 TreeSet 的性能比 LinkedList 好,LinkedList 的性能比 LinkedBlockingQueue 好。我希望我可以附上 Visual VM 结果。它不在文章中投票,http://www.ibm.com/developerworks/java/library/j-jtp10264/index.html

The operations are performed on

操作是在

Dell Vostro 1015, core 2 duo 2.10, 2GB Ram with 32 bit operating system and with

Dell Vostro 1015, core 2 duo 2.10, 2GB Ram with 32 bit operating system and with

JVM: Java HotSpot(TM) Client VM (20.1-b02, mixed mode) Java: version 1.6.0_26, vendor Sun Microsystems Inc.

JVM:Java HotSpot(TM) 客户端 VM(20.1-b02,混合模式) Java:版本 1.6.0_26,供应商 Sun Microsystems Inc.

回答by Kumar Vivek Mitra

1.ReentrantLockmight be more apt to use if you need to implement a thread that traverses a linked list, locking the next node and then unlocking the current node.

1.ReentrantLock如果你需要实现一个遍历链表的线程,锁定下一个节点,然后解锁当前节点,可能更容易使用

2.Synchronizedkeyword is apt in situation such as lock coarsening, provides adaptive spinning,biased locking and the potential for lock elision via escape analysis. Those optimizations aren't currently implemented for ReentrantLock.

2.Synchronized关键字适用于锁粗化等情况,提供自适应旋转、偏置锁定和通过逃逸分析消除锁的潜力。目前没有为 ReentrantLock 实现这些优化。

For a proper performance comparisonsee this:

有关适当的性能比较,请参见:

http://lycog.com/concurency/performance-reentrantlock-synchronized/

http://lycog.com/concurency/performance-reentrantlock-synchronized/

回答by JB Nizet

  1. Because your benchmark is flawed: in a real use-case, the time taken to produce and consume elements from the queue is much more important than the time it takes to add and remove an element to/from the queue. So the raw performance of the queue is not so important. BTW, the code only shows how you take elements from the first queue implementation, and not how you add them. Moreover, the choice of the appropriate structure is not made based on performance, but on behavior. If you want something concurrent, you choose a blocking queue, because it's implemented for you and doesn't have bugs like your code has. If you want FIFO (which is often what you want), you won't choose a TreeSet.

  2. If you want to compare synchronized vs. ReentrantLock, you shouldn't use one data structure for one, and another data structure for the other. ReentrantLock used to be faster, but they are on the same level, nowadays (if I believe what Brian Goetz says in JCIP). Anyway, I would choose one over the other for safety/capability reasons. Not for performance reasons.

  1. 因为您的基准测试存在缺陷:在实际用例中,从队列中生成和使用元素所花费的时间比向队列中添加和删除元素所花费的时间重要得多。所以队列的原始性能并不是那么重要。顺便说一句,代码只显示了如何从第一个队列实现中获取元素,而不是如何添加它们。此外,适当结构的选择不是基于性能,而是基于行为。如果你想要并发的东西,你可以选择一个阻塞队列,因为它是为你实现的,没有你的代码那样的错误。如果您想要 FIFO(这通常是您想要的),则不会选择 TreeSet。

  2. 如果您想比较 synchronized 与 ReentrantLock,您不应该将一种数据结构用于一种数据结构,而另一种数据结构用于另一种数据结构。ReentrantLock 过去更快,但现在它们处于同一水平(如果我相信 Brian Goetz 在 JCIP 中所说的话)。无论如何,出于安全/能力的原因,我会选择一个。不是出于性能原因。

回答by tibor17

I think I know where the problem is.

我想我知道问题出在哪里。

Since of Java 1.6 the lock in synchronizedblock is simply not locking if it is used in a single Thread. Therefore the execution of the code is not blocked while acquiring the lock. This optimization makes sense - one thread, no interaction with another thread - do nothing.

从 Java 1.6 开始,如果在单个线程中使用同步块中的锁,它就不会被锁定。因此在获取锁时不会阻塞代码的执行。这种优化是有道理的——一个线程,不与另一个线程交互——什么都不做。

There are two main scenarios to test:

有两个主要场景需要测试:

  1. test code executed in single Thread
  2. multiple Threads
  1. 单线程中执行的测试代码
  2. 多线程

Your test is executed in one thread and I guess the pseudolocks won't be optimized as the same way as the mutex locks. I guess they won't be optimized by the JIT.

您的测试是在一个线程中执行的,我猜伪锁不会像互斥锁那样进行优化。我猜他们不会被 JIT 优化。

The second alternative with multiple threads makes more sense to me, where the ReentrantLockis the winner, because it simply does not block all threads when acquiring the lock.

多线程的第二种选择对我来说更有意义,其中ReentrantLock是赢家,因为它在获取锁时不会阻塞所有线程。