简体   繁体   English

Synchronized 与 ReentrantLock 对性能的影响

[英]Synchronized vs ReentrantLock on performance

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.场景:- 1 个生产者,1 个消费者:- 生产者将一个整数放入队列中。 A consumer simply removes it from the queue.消费者只需将其从队列中删除。

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. 1) 当我通过 Visual VM 测量性能时,我观察到对于生产者代码,TreeSet 的性能优于 LinkedBlockingQueue 和 LinkedList,即使它需要 O(log n) 时间,但在 Linked 结构中创建对象是一个很大的开销. Why is the theory quite different to the practice ?为什么理论与实践大相径庭? Why do we prefer Linked, Array structures over Tree structures in queue implementations ?为什么在队列实现中我们更喜欢链接的数组结构而不是树结构?

2) The synchronized comes out as a clear winner vs the ReeentrantLock because TreeSet performed better than LinkedList which performed better than LinkedBlockingQueue. 2) synchronized 与 ReeentrantLock 相比明显胜出,因为 TreeSet 的性能优于 LinkedList,而 LinkedList 的性能优于 LinkedBlockingQueue。 I wish I could attach the Visual VM results.我希望我可以附上 Visual VM 结果。 It is not in votes with the article, http://www.ibm.com/developerworks/java/library/j-jtp10264/index.html这篇文章没有投票, 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,32 位操作系统和

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.

1. ReentrantLock might 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. Synchronized keyword is apt in situation such as lock coarsening, provides adaptive spinning,biased locking and the potential for lock elision via escape analysis . 2. Synchronized关键字适用于锁粗化等情况,提供自适应自旋、偏向锁以及通过逃逸分析消除锁的可能性 Those optimizations aren't currently implemented for ReentrantLock.目前没有为 ReentrantLock 实现这些优化。

For a proper performance comparison see this:有关适当的性能比较,请参见:

https://web.archive.org/web/20201022183359/https://blogs.oracle.com/dave/javautilconcurrent-reentrantlock-vs-synchronized-which-should-you-use https://web.archive.org/web/20201022183359/https://blogs.oracle.com/dave/javautilconcurrent-reentrantlock-vs-synchronized-which-should-you-use

  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.如果你想要 FIFO(这通常是你想要的),你不会选择 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.如果你想比较 synchronized 和 ReentrantLock,你不应该对一个数据结构使用一个数据结构,而对另一个数据结构使用另一个数据结构。 ReentrantLock used to be faster, but they are on the same level, nowadays (if I believe what Brian Goetz says in JCIP). ReentrantLock 过去更快,但现在它们处于同一水平(如果我相信 Brian Goetz 在 JCIP 中所说的话)。 Anyway, I would choose one over the other for safety/capability reasons.无论如何,出于安全/功能的原因,我会选择其中一个。 Not for performance reasons.不是出于性能原因。

I think I know where the problem is.我想我知道问题出在哪里。

Since of Java 1.6 the lock in synchronized block is simply not locking if it is used in a single Thread.从 Java 1.6 开始,如果在单个线程中使用同步块中的锁,它就不会被锁住。 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.这种优化是有意义的——一个线程,不与另一个线程交互——什么都不做。

There are two main scenarios to test:有两个主要场景需要测试:

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

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 ReentrantLock is the winner, because it simply does not block all threads when acquiring the lock.多线程的第二种选择对我来说更有意义,其中ReentrantLock是赢家,因为它在获取锁时不会阻塞所有线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM