我有3个帖子:2个消费者,ConsumerA
和ConsumerB
,以及Producer
。
我还有LinkedBlockingQueue queue
在t = 1时:ConsumerA
调用queue.take()
在t = 2时:Consumer
B调用queue.take()
在t = 3时:Producer
调用queue.put(foo)
是否保证ConsumerA在ConsumerB之前收到foo?换句话说,消费者调用take()
的顺序是每个通知的顺序?
如果没有,是否有替代数据结构可以根据订单提供更高的优先级?
答案 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队列使用不公平的锁。这样做可以让线程互相插入。虽然很少发生以下情况。
这是一种可能性。
答案 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$ConditionObject
,await()
和signal()
的正常同步变量调用。 1}}。
查看signalAll()
类的源代码,它保留了两个名为java.util.concurrent.locks.Condiion
和ConditionObject
的成员,它们是CLH锁定队列中的第一个和最后一个节点({{的实例) 1}})。
该课程中的文档说明:
线程可能会尝试获取它是否在队列中的第一个。但是,第一个并不能保证成功;它只给予抗争的权利。因此,当前发布的竞争者线程可能需要重新审核。
所以我相信这里的答案是firstWaiter
中的lastWaiter
方法试图优先处理那些之前调用java.util.concurrent.locks.AbstractQueuedSynchronizer$Node
的线程。它将给第一个线程调用take()
第一次获取队列项的机会,但由于超时,中断等,线程无法保证是第一个让项目脱离队列的线程。
请记住,这完全针对此特定实现。通常,您应该假设当队列项可用时,对LinkedBlockingQueue
的调用将唤醒随机等待线程,而不一定是第一个调用take()
的线程。