【发布时间】:2012-02-13 00:24:10
【问题描述】:
在我的一个 Java 6 应用程序中,我有一个线程向主线程提供数据,同时还从数据库中预取更多记录。它使用ArrayBlockingQueue 队列作为 FIFO 缓冲区,其主循环大致如下:
while (!Thread.interrupted()) {
if (source.hasNext()) {
try {
queue.put(source.next())
} catch (InterruptedException e) {
break;
}
} else {
break;
}
}
有一些代码会在循环终止后进行一些清理工作,例如毒化队列并释放任何资源,但这几乎就是全部。
就目前而言,从主线程到馈线线程没有直接通信:馈线线程使用适当的选项设置,然后使用阻塞队列自行离开控制数据流。
当队列已满时主线程需要关闭feeder时出现问题。由于没有直接控制通道,shutdown方法使用Thread接口到interrupt()feeder线程。不幸的是,在大多数情况下,尽管被中断,但馈线线程仍被阻止在 put() 中 - 不会引发异常。
通过对interrupt() 文档和队列实现源代码的简要阅读,在我看来put() 经常阻塞而不使用JVM 的任何可中断设施。更具体地说,在我当前的 JVM(OpenJDK 1.6b22)上,它阻塞了 sun.misc.Unsafe.park() 本机方法。也许它使用自旋锁或其他东西,但无论如何,这似乎属于the following case:
如果前面的条件都不成立,则设置该线程的中断状态。
设置了一个状态标志,但线程仍然在put() 中被阻塞并且不会进一步迭代以便可以检查该标志。结果?一个不会死的僵尸线程!
我对这个问题的理解是否正确,还是我遗漏了什么?
-
解决此问题的可能方法是什么?目前我只能想到两种解决方案:
一个。在队列中多次调用
poll()以解除对馈线线程的阻塞:就我所见,丑陋且不太可靠,但它大部分有效。b.使用带有超时的
offer()方法而不是put()以允许线程在可接受的时间范围内检查其中断状态。
除非我遗漏了什么,否则这是对 Java 中 BlockingQueue 实现的一些未充分记录的警告。 似乎在文档时有一些迹象,例如建议毒化队列以关闭工作线程,但我找不到任何明确的参考。
编辑:
好的,上面的解决方案 (a) 有一个更,呃,剧烈的变化:ArrayBlockingQueue.clear()。我认为这应该总是有效的,即使它不完全是优雅的定义......
【问题讨论】:
标签: java multithreading