28

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

  • 我想要同时访问队列,生产者推送,接收者弹出。
  • 当队列为空时,我希望消费者阻塞到队列中,等待生产者。

我的第一个想法是使用 a LinkedBlockingQueue,但我很快意识到它不是并发的并且性能受到影响。另一方面,我现在使用 a ,但我仍然为每个出版物ConcurrentLinkedQueue支付wait()/的费用。notify()由于消费者在找到一个空队列后不会阻塞,因此我必须同步并wait()锁定。另一方面,制作人必须notify()在每次发布时都获得该锁定。总体结果是 sycnhronized (lock) {lock.notify()},即使在不需要的时候,我也要为每一个出版物支付费用。

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

这样的事情可用/合理吗?

4

6 回答 6

12

java.util.concurrent.LinkedBlockingQueue我认为不管你有什么疑问,你都可以坚持下去。它是并发的。不过,我不知道它的性能。可能,其他实现BlockingQueue会更适合您。它们的数量并不多,因此请进行性能测试和测量。

于 2009-07-31T13:12:10.347 回答
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 省略):

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 块中刷新和关闭流。与其他解决方案相比,我不知道它的性能如何,但它非常通用。

于 2012-08-27T20:09:51.093 回答
5

我建议你看看ThreadPoolExecutor newSingleThreadExecutor。它将处理为您安排的任务,如果您将Callables提交给您的执行程序,您也将能够获得您正在寻找的阻塞行为。

于 2009-07-31T13:23:01.463 回答
4

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

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

于 2009-08-01T06:48:12.060 回答
3

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

于 2009-07-31T17:12:22.870 回答
2

这是实现的类列表BlockingQueue

我建议退房SynchronousQueue

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

于 2009-07-31T13:00:56.250 回答