我的问题与前面提到的this question有关。在我使用队列进行生产者和消费者线程之间的通信的情况下,人们通常会建议使用LinkedBlockingQueue
或ConcurrentLinkedQueue
吗?
使用一个优于另一个的优点/缺点是什么?
从API的角度来看,我可以看到的主要区别是LinkedBlockingQueue
可以选择性地限制。
答案 0 :(得分:97)
对于生产者/消费者线程,我不确定ConcurrentLinkedQueue
是否是一个合理的选择 - 它不实现BlockingQueue
,这是生产者/消费者队列IMO的基本接口。您必须致电poll()
,等待一段时间,如果您没有找到任何东西,然后再次轮询等...导致新项目进入时出现延迟,并且当出现空白时效率低下(由于醒来睡不着觉。)
来自BlockingQueue的文档:
BlockingQueue
实现主要用于生产者 - 消费者队列
我知道严格表示只有阻塞队列应该用于生产者 - 消费者队列,但即便如此......
答案 1 :(得分:60)
这个问题应该得到更好的答案。
Java ConcurrentLinkedQueue
基于着名的algorithm by Maged M. Michael and Michael L. Scott non-blocking lock-free队列。
"非阻塞"作为竞争资源(我们的队列)的术语,意味着无论平台的调度程序做什么,如中断线程,或者如果有问题的线程太慢,其他争用相同资源的线程将仍然能够进步。例如,如果涉及锁定,则可以中断持有锁定的线程,并且将阻止等待该锁定的所有线程。 Java中的内部锁(synchronized
关键字)也可能会对性能造成严重损失 - 比如涉及biased locking且确实存在争用时,或者在VM决定"膨胀后# 34;在旋转宽限期之后锁定并阻止竞争线程...这就是为什么在许多情况下(低/中争用的场景),对原子引用进行比较和设置可以更有效率,这正是许多非阻塞数据结构正在做。
Java ConcurrentLinkedQueue
不仅是非阻塞的,而且具有生产者不与消费者竞争的强大属性。在单一的生产者/单一消费者场景(SPSC)中,这实际上意味着没有争论可言。在多生产者/单一消费者场景中,消费者不会与生产者竞争。当多个生产者尝试offer()
时,此队列确实存在争用,但根据定义,该队列的并发性。它基本上是一个通用且高效的非阻塞队列。
至于它不是BlockingQueue
,阻塞线程在队列上等待是设计并发系统的一种非常糟糕的方式。唐'吨。如果你无法弄清楚如何在消费者/生产者场景中使用ConcurrentLinkedQueue
,那么只需切换到更高级别的抽象,就像一个好的角色框架。
答案 2 :(得分:23)
LinkedBlockingQueue
会阻止使用者或生产者。但是这种阻塞功能带来了成本:每个put或take操作都是生产者或消费者之间的争用(如果很多),所以在许多生产者/消费者的情况下,操作可能会更慢。
ConcurrentLinkedQueue
在其put / take操作中没有使用锁,但是CAS可能会减少许多生产者和消费者线程的争用。但作为一个“等待免费”的数据结构,ConcurrentLinkedQueue
在空时不会阻止,这意味着消费者需要通过“忙等待”来处理take()
返回null
值,例如,消费者线程占用CPU。
哪一个“更好”取决于消费者线程的数量,它们消耗/生产的速率等。每个方案都需要一个基准。
ConcurrentLinkedQueue
显然更好的一个特定用例是,当生产者首先生产某些东西并通过将工作放入队列并且仅在消费者开始消费之后完成他们的工作时,知道当队列为空时它们将完成。 (这里不是生产者 - 消费者之间的并发,而只是生产者 - 生产者和消费者 - 消费者之间的并发)
答案 3 :(得分:1)
另一个解决方案(扩展性不好)是集合点通道:java.util.concurrent SynchronousQueue
答案 4 :(得分:0)
如果您的队列不可扩展且只包含一个生产者/消费者线程。您可以使用无锁队列(您不需要锁定数据访问)。