9

考虑一个BlockingQueue和几个线程等待poll(long, TimeUnit)(可能也在 on take())。

现在队列是空的,需要通知等待的线程他们可以停止等待。预期的行为是null返回或声明的InterruptedException抛出。

Object.notify()LinkedBlockingQueue由于线程正在等待内部锁,因此无法正常工作。

有什么直截了当的方法吗?

4

3 回答 3

13

BlockingQueue 的 Javadoc 提出了一个好方法:

BlockingQueue 本质上不支持任何类型的“关闭”或“关闭”操作来指示不再添加项目。此类功能的需求和使用往往取决于实现。例如,一种常见的策略是生产者插入特殊的流尾或有毒对象,当消费者采用时会相应地解释这些对象。

于 2010-07-22T08:33:43.190 回答
5

常规的方法是中断线程,但这当然需要它们正确处理中断。

这意味着要围绕阻塞方法正确捕获和处理InterruptedExceptions,否则要定期检查(并采取行动)interrupted标志。

API 或语言规范中没有任何内容将中断与任何特定的取消语义联系起来,但在实践中,将中断用于除取消之外的任何事情都是脆弱的,并且难以在大型应用程序中维持。[...]

中断通常是实现取消的最明智的方式。

在第 7.1.1 节中说Java 并发实践。一个正确处理中断的示例,来自相同的(这是一个生产者线程,而不是消费者,但在当前上下文中这种差异可以忽略不计):

class PrimeProducer extends Thread {
    private final BlockingQueue<BigInteger> queue;

    PrimeProducer(BlockingQueue<BigInteger> queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            while (!Thread.currentThread().isInterrupted())
                queue.put(p = p.nextProbablePrime());
        } catch (InterruptedException consumed) {
            /*  Allow thread to exit  */
        }
    }
    public void cancel() { interrupt(); }
}

另一种解决方案是将超时参数设置为poll合理低,以便线程定期唤醒并且可以足够快地注意到中断。我仍然相信根据您的特定线程取消策略显式处理 InterruptedException 始终是一种好习惯。

于 2010-07-22T08:15:21.477 回答
1

我会说你的设计有问题。消耗 BlockingQueue 的线程不需要以这种方式中断。如果他们必须定期执行其他操作(例如检查变量的状态),同时还要从队列中消费,那么您应该使用 poll() 方法并相应地设置超时,以便两个操作可以交错。

于 2010-07-22T08:23:44.227 回答