这个问题应该得到更好的回答。 Java的ConcurrentLinkedQueue基于著名的algorithm by Maged M. Michael and Michael L. Scott,用于非阻塞无锁队列。 “非阻塞”在这里作为一个术语用于竞争资源(我们的队列),意味着无论平台的调度程序做什么,比如中断线程,或者如果有问题的线程太慢,其他竞争相同资源的线程仍然能够前进。例如,如果涉及到一个锁,持有该锁的线程可能会被中断,所有等待该锁的线程都将被阻塞。Java中的内在锁(synchronized关键字)也可能会对性能造成严重影响-例如当涉及到偏置锁定并且您确实存在争用时,或者在VM决定在旋转宽限期后“膨胀”锁并阻止争用线程之后。这就是为什么在许多上下文中(低/中争用的情形),对原子引用进行比较和设置可以更有效,并且这正是许多非阻塞数据结构正在做的事情。 Java的ConcurrentLinkedQueue不仅是非阻塞的,而且它还有一个令人敬畏的特性,即生产者不会与消费者竞争。在单生产者/单消费者场景(SPSC)中,这实际上意味着将不会有任何争用可言。在多个生产者/单个消费者场景中,消费者不会与生产者竞争。当多个生产者尝试offer()时,该队列确实会发生争用,但这是定义上的并发性。它基本上是一个通用的、高效的非阻塞队列。 至于它不是一个BlockingQueue,那么,阻塞一个线程来等待一个队列是设计并发系统的一种非常可怕的方法。如果你不知道如何在消费者/生产者场景中使用ConcurrentLinkedQueue,那么就切换到更高级别的抽象,比如一个好的actor框架。
6条答案
按热度按时间5kgi1eie1#
对于生产者/消费者线程,我甚至不确定
ConcurrentLinkedQueue
是一个合理的选择-它没有实现BlockingQueue
,这是生产者/消费者队列IMO的基本接口。你必须调用poll()
,如果你没有找到任何东西,等待一段时间,然后再次轮询等等。当有新物品进来时会导致延迟,当它是空的时会导致效率低下(由于不必要地从睡眠中醒来)。来自BlockingQueue的文档:
BlockingQueue
实现主要用于生产者-消费者队列我知道它并没有严格地说只有阻塞队列才应该用于生产者-消费者队列,但即使如此......
nfg76nw02#
这个问题应该得到更好的回答。
Java的
ConcurrentLinkedQueue
基于著名的algorithm by Maged M. Michael and Michael L. Scott,用于非阻塞无锁队列。“非阻塞”在这里作为一个术语用于竞争资源(我们的队列),意味着无论平台的调度程序做什么,比如中断线程,或者如果有问题的线程太慢,其他竞争相同资源的线程仍然能够前进。例如,如果涉及到一个锁,持有该锁的线程可能会被中断,所有等待该锁的线程都将被阻塞。Java中的内在锁(
synchronized
关键字)也可能会对性能造成严重影响-例如当涉及到偏置锁定并且您确实存在争用时,或者在VM决定在旋转宽限期后“膨胀”锁并阻止争用线程之后。这就是为什么在许多上下文中(低/中争用的情形),对原子引用进行比较和设置可以更有效,并且这正是许多非阻塞数据结构正在做的事情。Java的
ConcurrentLinkedQueue
不仅是非阻塞的,而且它还有一个令人敬畏的特性,即生产者不会与消费者竞争。在单生产者/单消费者场景(SPSC)中,这实际上意味着将不会有任何争用可言。在多个生产者/单个消费者场景中,消费者不会与生产者竞争。当多个生产者尝试offer()
时,该队列确实会发生争用,但这是定义上的并发性。它基本上是一个通用的、高效的非阻塞队列。至于它不是一个
BlockingQueue
,那么,阻塞一个线程来等待一个队列是设计并发系统的一种非常可怕的方法。如果你不知道如何在消费者/生产者场景中使用ConcurrentLinkedQueue
,那么就切换到更高级别的抽象,比如一个好的actor框架。zrfyljdw3#
LinkedBlockingQueue
在队列为空或满时阻塞消费者或生产者,并且相应的消费者/生产者线程被置于睡眠状态。但这种屏蔽功能是有代价的:每个put或take操作都是生产者或消费者(如果有很多)之间的锁争用,因此在具有许多生产者/消费者的场景中,操作可能会更慢。ConcurrentLinkedQueue
在它的add/poll操作中没有使用锁,而是使用了CAS,这可能会减少与许多生产者和消费者线程的争用。但是作为一个“无等待”的数据结构,ConcurrentLinkedQueue
在为空时不会阻塞,这意味着消费者将需要通过“忙碌等待”来处理返回null
值的poll()
,例如,消费者线程占用CPU。因此,哪一个“更好”取决于消费者线程的数量、它们消费/生产的速率等。每个场景都需要一个基准。
ConcurrentLinkedQueue
显然更好的一个特定用例是,生产者首先生产一些东西,并通过将工作放入队列来完成他们的工作,只有在之后,消费者才开始消费,知道他们将在队列为空时完成。(这里生产者-消费者之间没有并发,只有生产者-生产者和消费者-消费者之间的并发)pbpqsu0x4#
另一种解决方案(扩展性不好)是会合通道:java.util.concurrent SynchronousQueue
mzaanser5#
如果您的队列是不可扩展的,并且只包含一个生产者/消费者线程。您可以使用无锁队列(您不需要锁定数据访问)。
nbewdwxp6#
poll使用CAS来维护线程安全。
LinkedBlockingQueue.poll使用ReentrantLock!所以它更慢。但是提供了阻塞功能。
ConcurrentLinkedQueue是一个线程安全的链表。