Java:LinkedBlockingQueue是否考虑了消费者的顺序?

时间:2010-07-26 21:34:01

标签: java concurrency

我有3个帖子:2个消费者,ConsumerAConsumerB,以及Producer

我还有LinkedBlockingQueue queue

在t = 1时:ConsumerA调用queue.take()

在t = 2时:Consumer B调用queue.take()

在t = 3时:Producer调用queue.put(foo)

是否保证ConsumerA在ConsumerB之前收到foo?换句话说,消费者调用take()的顺序是每个通知的顺序?

如果没有,是否有替代数据结构可以根据订单提供更高的优先级?

3 个答案:

答案 0 :(得分:3)

从查看源代码来看,并不能保证。根据调度程序的感觉,随机唤醒一个线程,有一个防护块机制。

 notEmpty.signal(); // propagate to a non-interrupted thread

完整代码:http://kickjava.com/src/java/util/concurrent/LinkedBlockingQueue.java.htm

编辑:再看看ReenterantLock和Condition,显然是以FIFO顺序发出线程信号。因此,将首先发信号通知等待插入的第一个线程。但是,这些是实现细节!不要依赖它们。

  

不需要实现   定义完全相同的保证或   所有三种形式的语义   等待,也不需要支持   实际停工中断   线程

答案 1 :(得分:3)

在许多情况下,进入队列的线程顺序将是适当的顺序。但是,LinkedBlocking队列使用不公平的锁。这样做可以让线程互相插入。虽然很少发生以下情况。

  • 线程A进入并轮询。
  • 线程B输入尝试轮询 - 线程A保持锁定并停止线程B。
  • 线程A完成并发出信号B
  • 线程C在完全取消停放的情况下进入并获取B之前的锁
  • 线程B尝试轮询 - 线程C保持锁定并停止线程B。

这是一种可能性。

答案 2 :(得分:1)

深入挖掘Java 6源代码(如果你不关心找到负责这些东西的实际代码,请跳过水平规则之间的部分)

<小时/> 类java.util.concurrent.LinkedBlockingQueue使用java.util.concurrent.locks.ReentrantLock的实例实现互斥锁。

反过来,使用调用java.util.concurrent.locks.ReentrantLock.newCondition()的{​​{1}}生成同步变量。

方法java.util.concurrent.locks.ReentrantLock$Sync.newCondition()返回java.util.concurrent.locks.ReentrantLock$Sync.newCondition()的实例,该实例实现接口{java.util.concurrent.AbstractQueuedSynchronizer$ConditionObjectawait()signal()的正常同步变量调用。 1}}。


查看signalAll()类的源代码,它保留了两个名为java.util.concurrent.locks.CondiionConditionObject的成员,它们是CLH锁定队列中的第一个和最后一个节点({{的实例) 1}})。

该课程中的文档说明:

线程可能会尝试获取它是否在队列中的第一个。但是,第一个并不能保证成功;它只给予抗争的权利。因此,当前发布的竞争者线程可能需要重新审核。

所以我相信这里的答案是firstWaiter中的lastWaiter方法试图优先处理那些之前调用java.util.concurrent.locks.AbstractQueuedSynchronizer$Node的线程。它将给第一个线程调用take()第一次获取队列项的机会,但由于超时,中断等,线程无法保证是第一个让项目脱离队列的线程。

请记住,这完全针对此特定实现。通常,您应该假设当队列项可用时,对LinkedBlockingQueue的调用将唤醒随机等待线程,而不一定是第一个调用take()的线程。