首页 文章

Java中的并发和阻塞队列

提问于
浏览
27

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

  • 我希望并发访问队列, 生产环境 者推送,接收器弹出 .

  • 当队列为空时,我希望消费者阻塞队列,等待 生产环境 者 .

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

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

是这样的/合理的吗?

6 回答

  • 6

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

  • 3

    类似于这个答案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块中刷新和关闭流 . 与其他解决方案相比,我不知道它的表现如何,但它非常通用 .

  • 4

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

  • 5

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

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

  • 2

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

  • 12

    这是list of classes implementing BlockingQueue .

    我建议查看SynchronousQueue .

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

相关问题