LinkedBlockingQueue的Java性能问题

时间:2010-04-15 11:08:39

标签: java performance multithreading queue

这是我关于stackoverflow的第一篇文章...我希望有人可以帮助我

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

队列弹出并使用此类

中的静态方法调用方法
take()

如何调整public class C_myMessageQueue { private static final LinkedBlockingQueue<C_myMessageObject> x_queue = new LinkedBlockingQueue<C_myMessageObject>( 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(); } } 方法以实现至少25Mb / s,或者是否有其他类可以使用,当“队列”已满或为空时将阻止该类。

亲切的问候

巴特

P.S。:对不起我的英语不好,我来自德国;)

8 个答案:

答案 0 :(得分:16)

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

巩固我原来的答案,因为现在我们基本上已经完整了:

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

一些建议:

  • 尝试制作更粗粒度的对象,以便排队每个队列的开销与从生产线程卸载的实际工作相平衡(在您的情况下,您似乎为代表可忽略金额的对象创建了大量通信开销工作)
  • 你也可以让生产者通过卸载一些消费工作来帮助消费者(当有工作要做时,没有多少时间等待。)
在John W.正确地指出我的原始答案具有误导性之后

/更新了

答案 1 :(得分:3)

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

尝试使用ArrayBlockingQueue并测量性能。

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

答案 2 :(得分:3)

以下是一些可以尝试的事情:

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

如果生产者方面始终不在执行消费者方面,请考虑使用draindrainTo执行“批量”接收操作。

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

答案 3 :(得分:1)

很难说在不了解灌装过程的情况下会发生什么。

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

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

答案 4 :(得分:1)

发现this interesting post有关队列大小和垃圾回收导致的​​性能问题。

答案 5 :(得分:0)

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

尝试使用-XX:-UseBiasedLocking开关禁用它,看看是否有所作为。

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

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

如果从阻塞队列中放置和获取对象的原始性能开销是您的瓶颈(而不是slow-producer/consumer problem),则可以通过批处理对象获得巨大的性能提升:例如,而不是放置或处理好 - 粒度对象,放置或获取粗粒度的对象列表。这是一段代码:

<?php include("./header.inc"); ?> 
<div id="container">
    <div class="grid-sizer"></div>
        <?php if($page->numChildren(true)) {
            echo "<ul class='project'>";
            foreach($page->children as $childIndex => $child)  {
                if ($child->head_image) {
                    $image = $child->head_image;  
                    echo "<li class='item'><a href='#' data-featherlight='#mylightbox" . $childIndex . "'><img id='mylightbox" . $childIndex . "' src='{$image->url}' class='image'></a></li>"; 
                    }
                }  
        echo "</ul>";}
    ?>

<style>.featherlight-content:after {
        content:"<?php echo nl2br($child->title); ?> <?php echo nl2br($child->image_description); ?> <?php echo $child->formaat; ?>";}
                        </style>                

    </div> <!-- END CONTAINER -->
<script src="//code.jquery.com/jquery-latest.js"></script>
<script src="/site/templates/scripts/lightbox/release/featherlight.min.js" type="text/javascript" charset="utf-8"></script>

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