我有一个线程将事件推送到第二个线程的传入队列的经典问题 . 只有这一次,我对表现很感兴趣 . 我想要实现的是:
-
我希望并发访问队列, 生产环境 者推送,接收器弹出 .
-
当队列为空时,我希望消费者阻塞队列,等待 生产环境 者 .
我的第一个想法是使用 LinkedBlockingQueue
,但我很快就意识到它不是并发的,而且性能受到了影响 . 另一方面,我现在使用 ConcurrentLinkedQueue
,但我仍然在每个出版物上支付 wait()
/ notify()
的费用 . 由于消费者在找到空队列时没有阻止,因此我必须同步并锁定 . 另一方面,制作人必须在每一个出版物上获得锁定和 notify()
. 总体结果是,即使不需要,我也会在每一份出版物中支付 sycnhronized (lock) {lock.notify()}
的费用 .
我猜这里需要的是一个阻塞和并发的队列 . 我想 push()
操作在 ConcurrentLinkedQueue
中工作,当push元素是列表中的第一个时,对象的额外 notify()
. 我认为这样的检查已经存在于 ConcurrentLinkedQueue
中,因为推送需要连接下一个元素 . 因此,这比每次在外部锁上同步要快得多 .
是这样的/合理的吗?
6 回答
不管你有什么疑问,我认为你可以坚持到
java.util.concurrent.LinkedBlockingQueue
. 它是并发的 . 虽然,我不知道它的表现 . 可能,BlockingQueue
的其他实现将更适合您 . 其中没有太多,所以进行性能测试和测量 .类似于这个答案https://stackoverflow.com/a/1212515/1102730但有点不同..我最终使用
ExecutorService
. 您可以使用Executors.newSingleThreadExecutor()
实例化一个 . 我需要一个并发队列来读取/写入BufferedImages文件,以及读取和写入的原子性 . 我只需要一个线程,因为文件IO比源,网络IO快几个数量级 . 此外,我更关注动作的原子性和正确性而不是性能,但是这种方法也可以通过池中的多个线程来完成,以加快速度 .获取图像(Try-Catch-Finally省略):
保存图像(Try-Catch-Finally省略):
重要的是要注意,您应该在finally块中刷新和关闭流 . 与其他解决方案相比,我不知道它的表现如何,但它非常通用 .
我建议你看看ThreadPoolExecutor newSingleThreadExecutor . 它将处理为您订购的任务,如果您向执行者提交Callables,您将能够获得您正在寻找的阻止行为 .
你可以尝试jsr166中的LinkedTransferQueue:http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/
它满足您的要求,并且提供/轮询操作的开销更少 . 正如我从代码中看到的,当队列不为空时,它使用原子操作来轮询元素 . 当队列为空时,它会旋转一段时间并停止线程,如果不成功 . 我认为它可以帮助你的情况 .
每当我需要将数据从一个线程传递到另一个线程时,我就使用ArrayBlockingQueue . 使用put和take方法(如果满/空则会阻塞) .
这是list of classes implementing BlockingQueue .
我建议查看SynchronousQueue .
就像@Rorick在评论中提到的那样,我相信所有这些实现都是并发的 . 我认为您对
LinkedBlockingQueue
的担忧可能不合适 .