Java中的并发和阻塞队列

时间:2009-07-31 12:55:42

标签: java queue blocking concurrency

我有一个线程将事件推送到第二个线程的传入队列的经典问题。只有这一次,我对表现很感兴趣。我想要实现的是:

  • 我希望并发访问队列,生产者推送,接收器弹出。
  • 当队列为空时,我希望消费者阻止队列,等待生产者。

我的第一个想法是使用LinkedBlockingQueue,但我很快意识到它不是并发的,而且性能受到了影响。另一方面,我现在使用ConcurrentLinkedQueue,但我仍然在每个出版物上支付wait() / notify()的费用。由于消费者在找到空队列时没有阻止,我必须同步并wait()锁定。另一方面,生产者必须在每个出版物上获得锁定和notify()。总体结果是我付出了代价 每个出版物中sycnhronized (lock) {lock.notify()},即使不需要也是如此。

我想这里需要的是一个阻塞和并发的队列。我想象一个push()操作在ConcurrentLinkedQueue中工作,当推送元素是列表中的第一个时,对象的额外notify()。我认为这样的检查已经存在于ConcurrentLinkedQueue中,因为推送需要连接下一个元素。因此,这比每次在外部锁上同步要快得多。

这样的事情/合理吗?

6 个答案:

答案 0 :(得分:12)

我认为无论你有什么疑问,你都可以坚持java.util.concurrent.LinkedBlockingQueue。它是并发的。虽然,我不知道它的表现。可能BlockingQueue的其他实现更适合您。其中没有太多,所以进行性能测试和测量。

答案 1 :(得分:6)

与此答案类似https://stackoverflow.com/a/1212515/1102730但有点不同......我最终使用了ExecutorService。您可以使用Executors.newSingleThreadExecutor()实例化一个。我需要一个并发队列来读取/写入BufferedImages文件,以及读取和写入的原子性。我只需要一个线程,因为文件IO比源,网络IO快几个数量级。此外,我更关注动作的原子性和正确性而不是性能,但是这种方法也可以通过池中的多个线程来完成,以加快速度。

获取图像(Try-Catch-Finally省略):

Future<BufferedImage> futureImage = executorService.submit(new Callable<BufferedImage>() {
    @Override
        public BufferedImage call() throws Exception {
            ImageInputStream is = new FileImageInputStream(file);
            return  ImageIO.read(is);
        }
    })

image = futureImage.get();

保存图像(Try-Catch-Finally missing):

Future<Boolean> futureWrite = executorService.submit(new Callable<Boolean>() {
    @Override
    public Boolean call() {
        FileOutputStream os = new FileOutputStream(file); 
        return ImageIO.write(image, getFileFormat(), os);  
    }
});

boolean wasWritten = futureWrite.get();

重要的是要注意你应该在finally块中刷新和关闭你的流。与其他解决方案相比,我不知道它的表现如何,但它非常通用。

答案 2 :(得分:5)

我建议你看看ThreadPoolExecutor newSingleThreadExecutor。它将处理为您订购的任务,如果您向执行者提交Callables,您将能够获得您正在寻找的阻止行为。

答案 3 :(得分:4)

您可以尝试jsr166中的LinkedTransferQueue:http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/

它满足您的要求,并且提供/轮询操作的开销更少。 正如我从代码中看到的,当队列不为空时,它使用原子操作来轮询元素。当队列为空时,它会旋转一段时间并停止线程,如果不成功。 我认为这对你的情况有帮助。

答案 4 :(得分:3)

每当我需要将数据从一个线程传递到另一个线程时,我都会使用ArrayBlockingQueue。使用put和take方法(如果满/空将阻止)。

答案 5 :(得分:2)

这是list of classes implementing BlockingQueue

我建议您查看SynchronousQueue

就像@Rorick在评论中提到的那样,我相信所有这些实现都是并发的。我认为您对LinkedBlockingQueue的担忧可能不合适。