对于多线程系统的Queue实现,我遇到了一些惊喜。这是: -
情景: - 1个生产者,1个消费者: - 生产者将整数放入队列中。消费者只是将其从队列中删除。
队列的基础数据结构: - TreeSet(我从未想过会使用),LinkedList,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: - 与synchronized相似的代码。
问题: -
1)当我通过Visual VM测量性能时,我发现对于生产者代码,TreeSet比LinkedBlockingQueue和LinkedList表现更好,即使它需要O(log n)时间,在Linked结构中创建对象也是一个重大的开销。为什么理论与实践有很大不同?为什么我们更喜欢队列实现中的Tree结构的Linked,Array结构?
2)同步作为ReeentrantLock的明显赢家出现,因为TreeSet比LinkedList表现更好,而LinkedList的表现优于LinkedBlockingQueue。我希望我可以附加Visual VM结果。它不是与文章http://www.ibm.com/developerworks/java/library/j-jtp10264/index.html
一起投票操作在
上执行戴尔Vostro 1015,核心2双核2.10,2GB Ram,32位操作系统和
JVM:Java HotSpot(TM)客户端VM(20.1-b02,混合模式) Java:版本1.6.0_26,供应商Sun Microsystems Inc.
答案 0 :(得分:16)
1。 ReentrantLock
可能更容易使用。 强>
2。 Synchronized
关键字适用于锁定粗化,提供自适应旋转,偏向锁定以及通过转义分析锁定省略的可能性。目前,ReentrantLock尚未实施这些优化。
对于正确的效果比较,请参阅:
http://lycog.com/concurency/performance-reentrantlock-synchronized/
答案 1 :(得分:5)
因为您的基准测试存在缺陷:在实际用例中,从队列中生成和使用元素所花费的时间比向队列添加元素和从队列中删除元素所花费的时间要重要得多。因此队列的原始性能并不那么重要。顺便说一下,代码只显示了如何从第一个队列实现中获取元素,而不是如何添加元素。而且,选择合适的结构不是基于性能,而是基于行为。如果你想要并发的东西,你选择一个阻塞队列,因为它是为你实现的,并没有像你的代码那样的bug。如果你想要FIFO(通常是你想要的),你就不会选择TreeSet。
如果您想比较synchronized和ReentrantLock,您不应该为一个使用一个数据结构,而为另一个使用另一个数据结构。 ReentrantLock曾经更快,但现在它们处于同一水平(如果我相信Brian Goetz在JCIP中所说的话)。无论如何,出于安全/能力的原因,我会选择一个而不是另一个。不是出于性能原因。
答案 2 :(得分:0)
我想我知道问题出在哪里。
从Java 1.6开始,如果在单个线程中使用 synchronized 块中的锁,则该锁只是简单地未锁定。因此,在获取锁时不会阻止代码的执行。这种优化是有意义的-一个线程,没有与另一个线程的交互-什么都不做。
有两种主要测试场景:
您的测试在一个线程中执行,我想伪锁不会像互斥锁那样被优化。我想JIT不会对它们进行优化。
具有多个线程的第二个选择对我来说更有意义,因为 ReentrantLock 是赢家,因为它在获取锁时不会阻塞所有线程。