2012-07-22 54 views
12

当涉及到多线程系统的队列实现时,我已经经历了一些惊喜。这里是: -同步vs ReentrantLock的性能

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

队列的底层数据结构: - TreeSet中(但决没有想到我将使用),链表,的LinkedBlockingQueue(具有不定尺寸)

的代码: - 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; 
      } 
     } 
    } 

编辑: -

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

对于的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(); 
     } 
    } 

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

的问题: -

1)当我测量通过Visual VM的性能,我观察到,对于生产者代码,TreeSet中的性能比的LinkedBlockingQueue和LinkedList更好,即使它需要O(log n)的时间,在链接结构中创建对象是一个很大的开销。为什么理论与实践有很大的不同?为什么我们喜欢在队列实现中的树结构上使用Linked和Array结构?

2)同步出来作为一个明确的胜利者与ReeentrantLock,因为TreeSet执行比LinkedList更好,比LinkedBlockingQueue表现更好。我希望我可以附加Visual VM结果。这是不符合文章票,http://www.ibm.com/developerworks/java/library/j-jtp10264/index.html

的操作是在

戴尔Vostro 1015进行的Core 2 Duo 2.10,2GB内存与32位操作系统,并与

JVM:Java的热点( TM)客户端VM(20.1-B02,混合模式) 的Java:版本1.6.0_26,供应商Sun微系统公司

回答

13

ReentrantLock可能会更容易,如果你需要执行一个线程来使用遍历链表,锁定下一个节点,然后解锁当前节点。

2.Synchronized关键字易于在情况如锁粗,提供自适应纺丝,偏向锁和经由逃逸分析为锁省略的可能性。这些优化目前尚未针对ReentrantLock实施。

对于适当的性能比较看到这一点:

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

+0

这几乎是我在我的问题中附加链接的同一个例子。它告诉我REL的性能比2线程开始的同步要好。另外我觉得,如果我有办法强制OS调度程序在所有可用的CPU上运行线程,我们肯定会获得更好的LinkedBlockingQueues性能。如果你需要我的观察数据,请告诉我。 – 100pipers 2012-07-22 13:55:04

+2

库马尔忘记提及他从[David Dice的博客](https://blogs.oracle.com/dave/entry/java_util_concurrent_reentrantlock_vs)中回答了他的答案。 – 2013-06-17 05:46:07

+0

@AlexanderRyzhov感谢让我指向本文的原作者,因为当我从其他博客为我的scjp做准备时阅读此内容...非常感谢您 – 2013-06-17 05:55:19

4
  1. 因为你的基准是有缺陷的:在真实的使用情况,所花费的时间从队列中生产和消费元素比向队列添加元素和从队列中移除元素所花费的时间要重要得多。所以队列的原始表现并不那么重要。顺便说一句,代码只显示了如何从第一个队列实现中获取元素,而不是如何添加它们。此外,选择合适的结构不是基于表现而是基于行为。如果你想要并发的东西,你选择一个阻塞队列,因为它已经为你实现了,并且没有像你的代码那样的错误。如果你想要FIFO(这通常是你想要的),你不会选择一个TreeSet。

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

+0

我不同意决策标准不应考虑绩效。一个典型的方法是让解决方案正常工作,然后考虑性能。但事先考虑它绝对没有错,因为它可以帮助节省以后耗时的重构。 – Brady 2012-07-22 13:39:31

+0

@JB Nizet: - 我的代码包含什么错误?我有类似的理解,对于FIFO,我不应该使用TreeSet,但告诉我,如果TreeSet的put方法比LinkedList的add方法快得多(因为对象构建时间很长),为什么不能我是否使用TreeSet(虽然在LinkedList的情况下删除速度非常快)?对于实时交易应用程序,只有表现很重要。因此,故事的关键是我想使用更好的DS队列,并且我想使用REL来同步。 – 100pipers 2012-07-22 14:01:44

+1

@Brady当然,不要从一个愚蠢的设计开始,这个设计显然会表现不佳,但是您不应该选择基于性能预感的特定实现。选择最适合您的应用程序需求的应用程序,并且您将获得更少的错误。当且仅当性能成为*问题*时,寻找方法通过交换实现来改进它。 – Bohemian 2012-07-22 14:04:38