LinkedBlockingQueue的Java性能问题

这是我在stackoverflow上的第一篇文章…我希望有人可以帮助我

我使用Java 6 LinkedBlockingQueue进行了很大的性能回归。 在第一个线程中,我生成了一些我推入队列的对象。在第二个线程中,我将这些对象拉出来。 当频繁调用LinkedBlockingQueuetake()方法时, take()发生性能回归。 我监控整个程序, take()方法总体上take()了最多的时间。 吞吐量从~58Mb / s到0.9Mb / s ……

队列弹出并使用此类中的静态方法调用方法

 public class C_myMessageQueue { private static final LinkedBlockingQueue x_queue = new LinkedBlockingQueue( 50000 ); /** * @param message * @throws InterruptedException * @throws NullPointerException */ public static void addMyMessage( C_myMessageObject message ) throws InterruptedException, NullPointerException { x_queue.put( message ); } /** * @return Die erste message der MesseageQueue * @throws InterruptedException */ public static C_myMessageObject getMyMessage() throws InterruptedException { return x_queue.take(); } } 

如何调整take()方法以达到至少25Mb / s,或者是否有一个我可以使用的类,它将在“队列”满或空时阻塞。

亲切的问候

巴特

PS:抱歉我的英语不好,我来自德国;)

您的生产者线程只是放置了比消费者消耗的元素更多的元素 ,因此队列最终达到其容量限制,因此生产者等待。

从现在起巩固我原来的答案我们基本上已经全面了解:

  • 你通过执行非常快的put()来达到LinkedBlockingQueue的固有吞吐量限制(每个队列都有一个),即使是连续的take()s ,没有进一步处理,也无法跟上。 (顺便说一下,这表明在这个结构中,无论如何,在你的JVM和机器上,put()s至少比读取的成本稍高一些)。
  • 由于存在消费者锁定的特定锁定,因此放置更多的消费者线程可能无法提供帮助(如果您的消费者实际上正在进行某些处理并且限制了吞吐量,那么添加更多消费者将有所帮助。对于具有更好的队列实现不止一个消费者(或生产者),您可以尝试SynchronousQueueConcurrentLinkedQueue和即将推出的jsr166y的TransferQueue )。

一些建议:

  • 尝试制作更粗粒度的对象,以便排队每个对象的开销与从生成线程卸载的实际工作相平衡(在您的情况下,似乎您为代表可忽略不计的工作量的对象创建了很多通信开销)
  • 你也可以让生产者通过卸载一些消费工作来帮助消费者(当有工作要做时,没有多少时间等待。)

/在John W.正确地指出我的原始答案是误导之后更新

我通常建议不要在性能敏感的代码区域中使用LinkedBlockingQueue,使用ArrayBlockingQueue。 它将提供更好的垃圾收集配置文件,并且比LinkedBlockingQueue更加缓存友好。

尝试使用ArrayBlockingQueue并测量性能。

LinkedBlockingQueue的唯一优点是它可以是无界的,但这很少是你真正想要的。 如果您遇到消费者失败并且队列开始备份的情况,那么拥有有界队列会使系统优雅地降级,而不是冒险在队列无界时可能发生的OutOfMemoryErrors。

这里有几件事要尝试:

LinkedBlockingQueue替换为ArrayBlockingQueue 。 它没有悬空引用,因此在队列填满时表现更好。 具体来说,鉴于LinkedBlockingQueue的1.6实现,在队列实际变空之前,不会发生元素的完整GC。

如果生产者方面一直在执行消费者方面,请考虑使用draindrain来执行“批量”采取操作。

或者,让队列获取消息对象的数组或列表。 生成器使用消息对象填充List或数组,每个put或take移动具有相同锁定开销的多个消息。 把它想象成一个秘书递给你一堆“当你外出”的消息,而不是一次一个地交给你。

如果不了解填充过程的内容,很难说会发生什么。

如果不经常调用addMyMessage – 可能是因为应用程序的整个不同部分存在性能问题 – take方法必须等待。

这样看起来像是罪魁祸首,但实际上它是你的应用程序的填充部分。

找到这篇关于队列大小和垃圾收集导致的性能问题的有趣post 。

您的应用程序可能会受到Java 6中与锁定相关的更改的影响,尤其是“偏向锁定”function。

尝试使用-XX:-UseBiasedLocking开关禁用它,看看是否-XX:-UseBiasedLocking

有关详细信息,请参阅此处 : http : //java.sun.com/performance/reference/whitepapers/6_performance.html

不能肯定地告诉任何事情。 但您可以尝试更改BlockingQueue实现(就像实验一样)。

您将初始容量设置为50k并使用LinkedBlockingQueue 。 尝试使用相同容量的ArrayBlockingQueue ,您也可以使用fair参数。

如果从阻塞队列中放置和获取对象的原始性能开销是您的瓶颈(而不是缓慢的生产者/消费者问题 ),则可以通过批处理对象来获得巨大的性能提升:例如,不要使用或采用细粒度对象,您放置或采用粗粒度的对象列表。 这是一段代码:

 ArrayBlockingQueue> Q = new ArrayBlockingQueue>(); // producer side List l = new ArrayList(); for (int i=0; i<100; i++) { l.add(i); // your initialization here } Q.put(l); // consumer side List l2 = Q.take(); // do something 

批处理可以将您的性能提升一个数量级。