【发布时间】:2018-08-14 19:14:31
【问题描述】:
在以下场景中,终结器线程必须等待消费者线程处理完所有队列元素才能完成执行:
private final BlockingQueue<Object> queue = new LinkedBlockingQueue<>();
private final Object queueMonitor = new Object();
// Consumer thread
while (true) {
Object element = queue.take();
consume(element);
synchronized (queueMonitor) {
queueMonitor.notifyAll();
}
}
// Finalizer thread
synchronized (queueMonitor) {
while (!queue.isEmpty()) {
queueMonitor.wait();
}
}
元素会随着时间的推移添加到队列中。 消费者守护线程一直运行,直到 JVM 终止,此时必须允许它完成对所有排队元素的处理。 目前这是由终结器线程完成的,它是一个关闭钩子,应该延迟在 JVM 终止时杀死消费者线程。
问题:
如果在从队列中取出最后一个元素后启动终结器线程,则 while 循环条件的计算结果为 false,因此在 consume() 尚未返回时执行完成,因为完全跳过了等待 queueMonitor。
研究:
一个理想的解决方案是peek the queue,然后在元素被消耗后将其删除。
【问题讨论】:
-
您是说您希望“终结器”线程在“消费者”线程完成之前什么都不做?在这种情况下,为什么不在一个线程中完成这两项工作呢?
-
如果你的“消费者”线程不是守护进程怎么办?如果它不是
while(true),而是循环直到在队列中找到一颗毒丸,然后退出呢?然后,无论您调用什么函数来关闭应用程序,它都可以将毒丸送入队列,然后join消费者。 -
@jameslarge 没有调用函数来关闭应用程序。该代码是库的一部分,我不希望客户端调用 API 来中断非守护线程并让 JVM 终止,尽管这是一个选项。
-
有什么理由不将消费者中的整个 while 循环包装在同步块中锁定
queueMonitor以使终结器块直到消费者完成?我想说整个设置看起来很容易出错,我建议使用CountDownLatch之类的东西,如下所示。 -
@JanusVarmarken 原因是消费者线程永远不会完成。它是一个在 JVM 终止时被杀死的守护线程。
标签: java multithreading concurrency blockingqueue