在Java Concurrency In Practice一书(第156页)中,有关于毒丸方法的声明:
毒丸只能在未绑定的队列中可靠地工作。
这是否意味着有限的队列我可以陷入僵局,还是其他一些活动问题呢? 它与生产者和顾客的数量有关吗?
答案 0 :(得分:7)
使用有限的队列,可以防止添加毒丸。
避免此问题的一种方法是在添加毒丸时使有界队列再允许一个。
答案 1 :(得分:6)
问题是队列在关闭时可能已满。
这取决于关闭时队列中数据的价值。你能负担得起把所有东西扔进去吗?
当关闭队列时,在添加毒丸之前先排空队列应该是有效的。
void close () throws InterruptedException {
do {
// Empty the queue.
while ( queue.poll(0,TimeUnit.MILLISECONDS) != null ) {
// Throw it away.
}
// Keep draining the queue 'till the pill is swallowed.
} while (!queue.offer(STOP, 0, TimeUnit.MILLISECONDS)) ;
}
但当然如果队列中的项目很有价值,您可能希望使用drainto
并保留它们。
还请记住,在毒丸之后可能会有更多项目添加到队列中,因为不仅队列可能已满,而且可能还有线程被阻止等待发布。
答案 2 :(得分:1)
@gstackoverflow:有界队列的主要问题是它具有最大容量,因此如果您的有界队列已满,当您想要添加此“毒丸”时,您将被阻止。
请记住,毒丸必须立即放置,不能等到队列有一些空间,因为这种技术用于在发生异常时正常关闭消费者(否则存在更好的情况)关闭消费者的技术)。
编辑:举个例子说一千多个句子,让我们看一个简单的例子(示例中的所有学分转到实践中的Java并发),并带有一个制作人线程和消费者线程:
public class CrawlerThread extends Thread { //The Producer Thread
public void run() {
try {
crawl(root);
} catch (InterruptedException e) { /* fall through */ }
finally {
while (true) {
try {
queue.put(POISON);
break;
} catch (InterruptedException e1) { /* retry */ }
}
}
}
private void crawl(File root) throws InterruptedException {
//some code
}
}
public class IndexerThread extends Thread { //The consumer Thread
public void run() {
try {
while (true) {
File file = queue.take();
if (file == POISON)
break;
else
indexFile(file);
}
} catch (InterruptedException consumed) { }
}
}
现在,当您检查生产者线程(CrawlerThread)时,您会看到Poison药丸放置在运行结束时,或者在发生异常时处于更可怕的情况。
现在假设您想使用有界队列作为生产者和消费者之间的接口,假设队列在时间t已满,并且生产者在时间t发生异常,生产者将不会能够将Poison药丸放在队列中而不是让Consumer线程关闭,消费者仍然会等待元素进入队列。因此,如果您使用有界队列,则不建议使用Poison pill方法,因为它可能会导致意外结果。
答案 3 :(得分:0)
我认为Java Concurrency In Practice的作者可能试图这样说:
将新邮件添加到完整有界队列时,新邮件或其中一条现有邮件会被删除。因此,应用程序可以在它们成为下一个可用消息之前永久地连续丢弃中毒药片。
但是可以添加逻辑。例如,我们可以强制执行中毒药丸,并且永远不会被删除。在这种情况下,有界队列实际上可以正常工作。