Answers:
对于生产者/消费者线程,我不确定这ConcurrentLinkedQueue
是否是一个合理的选择-它没有实现BlockingQueue
,这是生产者/消费者队列IMO的基本接口。您必须打电话poll()
,如果您什么都没找到,请稍等,然后再次轮询等...导致新项目进入时出现延迟,而新项目空着时效率低下(由于从睡眠中不必要地唤醒) 。
从BlockingQueue的文档中:
BlockingQueue
实现被设计为主要用于生产者-消费者队列
我知道这并不是严格说生产者-消费者队列只应使用阻塞队列,但即使如此……
ConcurrentLinkedQueue
如果您的线程正在检查多个队列,则A 也很有用。例如,在多租户服务器中。出于隔离的原因,假设您不使用单个阻塞队列,而是使用租户区分符。
take()
且put()
仅消耗比额外的资源(同步项)时,您的情况才成立ConcurrentLinkedQueue
。尽管在生产者-消费者方案中
这个问题值得一个更好的答案。
Java ConcurrentLinkedQueue
基于Maged M. Michael和Michael L. Scott的著名算法,用于无阻塞无锁队列。
“无阻塞”在这里是争用资源(我们的队列)的术语,意味着无论平台的调度程序做什么,例如中断线程,或者所讨论的线程太慢,其他线程都在争用同一资源仍然可以进步。例如,如果涉及一个锁,则持有该锁的线程可能会中断,并且所有等待该锁的线程都将被阻塞。synchronized
Java中的内在锁(关键字)也可能带来严重的性能损失-例如偏向锁定时涉及到您并且确实存在争用,或者在VM决定在旋转宽限期之后“膨胀”锁并阻塞竞争线程之后……这就是为什么在许多情况下(低/中争用的情况)进行比较和原子引用上的-sets可以更加高效,而这正是许多非阻塞数据结构正在做的事情。
Java ConcurrentLinkedQueue
不仅具有非阻塞性,而且具有生产者无法与消费者抗衡的强大特性。在单一生产者/单一消费者场景(SPSC)中,这实际上意味着没有争执。在多生产者/单一消费者的情况下,消费者将不与生产者竞争。当多个生产者尝试时offer()
,此队列确实有争用,但是根据定义,这是并发的。基本上,这是一个通用且高效的非阻塞队列。
至于不是BlockingQueue
,阻止线程等待队列是设计并发系统的一种非常糟糕的方式。别。如果您不知道如何ConcurrentLinkedQueue
在消费者/生产者场景中使用,则只需切换到更高级别的抽象,例如好的actor框架。
LinkedBlockingQueue
当队列为空或已满并且相应的使用者/生产者线程进入睡眠状态时,阻止使用者或生产者。但是,这种阻塞功能会带来成本:每个生产者或消费者(如果有很多)之间的竞争都是锁定的,因此在生产者/消费者很多的情况下,操作可能会变慢。
ConcurrentLinkedQueue
在其put / take操作上未使用锁,而是使用CAS,有可能减少与许多生产者和使用者线程的争用。但是,作为“无等待”数据结构,ConcurrentLinkedQueue
在空时不会阻塞,这意味着使用者将需要通过“繁忙等待” 来处理take()
返回null
值,例如,使用者线程耗尽了CPU。
因此,哪个“更好”取决于使用者线程的数量,使用者消耗/生产的速率等。每种情况都需要一个基准。
一个ConcurrentLinkedQueue
明显更好的特定用例是,生产者首先生产产品并通过将工作放入队列中来完成工作,并且只有在消费者开始消费之后才知道,当队列为空时他们将完成工作。(这里生产者-消费者之间没有并发性,只有生产者-生产者和消费者-消费者之间并发)
unbounded blockingqueue
使用的话会好于CAS基于并行ConcurrentLinkedQueue
另一个解决方案(无法很好地扩展)是集合通道:java.util.concurrent SynchronousQueue