LinkedBlockingQueue 与 ConcurrentLinkedQueue 的比较

我的问题与前面提到的 这个问题有关。在使用队列进行生产者线程和消费者线程之间通信的情况下,人们通常会推荐使用 LinkedBlockingQueueConcurrentLinkedQueue吗?

使用一种方法比使用另一种方法有什么优缺点?

从 API 的角度来看,我可以看到的主要区别是 LinkedBlockingQueue可以是可选的有界的。

65858 次浏览

For a producer/consumer thread, I'm not sure that ConcurrentLinkedQueue is even a reasonable option - it doesn't implement BlockingQueue, which is the fundamental interface for producer/consumer queues IMO. You'd have to call poll(), wait a bit if you hadn't found anything, and then poll again etc... leading to delays when a new item comes in, and inefficiencies when it's empty (due to waking up unnecessarily from sleeps).

来自 BlockingQueue 的文档:

BlockingQueue实现主要用于生产者-使用者队列

我知道 严格来说没有说只有阻塞队列应该用于生产者-消费者队列,但即便如此..。

Another solution (that does not scale well) is rendezvous channels : java.util.concurrent SynchronousQueue

如果您的队列是不可扩展的,并且只包含一个生产者/消费者线程。可以使用无锁队列(不需要锁定数据访问)。

LinkedBlockingQueue blocks the consumer or the producer when the queue is empty or full and the respective consumer/producer thread is put to sleep. But this blocking feature comes with a cost: every put or take operation is lock contended between the producers or consumers (if many), so in scenarios with many producers/consumers the operation might be slower.

ConcurrentLinkedQueue在添加/轮询操作中使用的不是锁,而是 民安队,这可能会减少与许多生产者和消费者线程的争用。但是作为一个“无等待”的数据结构,ConcurrentLinkedQueue在为空时不会阻塞,这意味着使用者需要通过“繁忙等待”来处理返回 null值的 poll(),例如,使用者线程占用 CPU。

因此,哪一个更好取决于使用者线程的数量,取决于它们消耗/生产的速度等等。每个场景都需要一个基准测试。

One particular use case where the ConcurrentLinkedQueue is clearly better is when producers first produce something and finish their job by placing the work in the queue 而且是在那之后 the consumers starts to consume, knowing that they will be done when queue is empty. (here is no concurrency between producer-consumer but only between producer-producer and consumer-consumer)

这个问题值得更好的回答。

Java 的 ConcurrentLinkedQueue基于著名的 算法由魔法师 M。迈克尔和迈克尔 L。斯科特 for 无阻塞无锁队列。

“非阻塞”作为争用资源(我们的队列)的术语在这里意味着无论平台的调度器做什么,比如中断一个线程,或者如果有问题的线程太慢,其他争用同一资源的线程仍然能够进行。例如,如果涉及到一个锁,持有该锁的线程可能被中断,等待该锁的所有线程都将被阻塞。Java 中的内部锁(synchronized关键字)也会对性能造成严重的影响——比如当涉及到 偏置锁定偏置锁定并且你确实有争用时,或者在 VM 决定在旋转宽限期之后“膨胀”锁并阻塞争用线程之后... ... 这就是为什么在许多情况下(低/中等争用的场景) ,在原子引用上进行比较集可以更有效率,这正是许多非阻塞数据结构正在做的事情。

Java 的 ConcurrentLinkedQueue不仅是非阻塞的,而且它具有生产者无法与消费者竞争的可怕特性。在单一生产者/单一消费者场景(SPSC)中,这实际上意味着不存在争论。在多个生产者/单个消费者场景中,消费者不会与生产者竞争。当多个生产者尝试 offer()时,这个队列确实存在争用,但从定义上讲,这就是并发。它基本上是一个通用的、高效的非阻塞队列。

至于它不是 BlockingQueue,那么,阻塞线程以等待队列是设计并发系统的一种极其糟糕的方法。不要。如果您不知道如何在消费者/生产者场景中使用 ConcurrentLinkedQueue,那么只需切换到更高级别的抽象,比如一个好的角色框架。