3

我正在阅读Goetz 的 Java Concurrency In Practice,其中显示了此示例代码:

public final class Indexer implements Runnable {

    private final BlockingQueue<File> queue;

    public Indexer(BlockingQueue<File> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            while (true) {
                queue.take();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

}

与描述:

恢复中断。有时您不能抛出 InterruptedException,例如当您的代码是 Runnable 的一部分时。在这些情况下,您必须捕获 InterruptedException 并通过在当前线程上调用中断来恢复中断状态,以便调用堆栈更高的代码可以看到发出了中断,如清单 5.10 所示。

在示例代码中,如果执行此代码,“调用堆栈上方的代码”将永远不会看到中断 - 还是我做错了推论?这里的线程在调用后就死了interrupt(),对吗?

所以这interrupt()可能有用的唯一方法是如果它在一个循环中,对吗?

4

4 回答 4

2

这里的线程在调用中断()后就死了,对吗?

执行线程不会在中断后完成,你在想Thread#stop。即使在可运行对象完成后,线程线程也可能继续运行。Runnable 只是一个线程运行的任务。

该任务完成后,线程知道发生中断是否重要?如果线程需要响应其他一些取消请求并且正在由另一个线程完成,该怎么办?

因为您只是一个 Runnable 任务,所以您没有这些答案,因此您应该让线程知道确实发生了中断,以便线程可以按照它想要的方式处理它。

于 2016-07-21T17:37:20.370 回答
1

在编写玩具示例时,我一直对此感到矛盾,我讨厌Thread.currentThread().interrupt()在示例中不做任何事情时加入类似的东西,但我也希望我的示例能够展示良好的实践,这意味着设置中断标志。

如果您将 Runnable 传递给 Thread ,那么显然没有什么可以读取作为 Runnable 完成的最后一件事恢复的中断。当线程终止时,中断标志值不再可用。

如果将 Runnable 传递给 Executor,则 Executor 负责在其线程上设置中断标志。此外,执行者应该知道不要依赖任务来恢复中断标志。因此,如果 Executor 任务不恢复中断状态,通常无关紧要。

但可能存在边缘情况;Runnable 可能需要由当前线程运行,而不是移交给另一个线程。例如,如果您将任务提交给执行器,该执行器具有使任务在调用线程中运行的拒绝策略,那么如果它正在运行被执行器拒绝的任务,则调用线程的中断可能会丢失恢复中断标志。

最好能够更改执行器的配置,而不必担心该配置的任务是否表现良好。任务是泄漏的抽象,这通常是不可能的,但对于这个特定的问题,它是:让你的任务在所有情况下恢复中断标志,只是为了安全。

于 2016-09-20T12:15:51.477 回答
1

在 Java 中,您需要处理线程中断。为此,Thread.isInterrupted()有必要进行轮询。当您的代码是 a 的一部分时,Runnable您需要设置 Thread 的中断标志,InterruptedException以便该线程运行的后续任务(调用堆栈更高的代码)可以Thread.isInterrupted()适当地轮询和处理中断。这也使线程有机会干净地退出而不是立即杀死它。这就是这样Thread.stop()做的。

Object.wait并且Thread.sleep是两个示例,首先检查标志并InterruptedException在设置时抛出一个。

于 2016-09-20T11:27:38.673 回答
0

Java 文档指定了Thread.interrupt()as

如果该线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法或 join()、join(long)、join(long, int) 时被阻塞, sleep(long), or sleep(long, int), 这个类的方法,那么它的中断状态会被清除并且会收到一个InterruptedException。

如果该线程在 InterruptibleChannel 上的 I/O 操作中被阻塞,则通道将关闭,线程的中断状态将被设置,线程将收到 ClosedByInterruptException。

如果该线程在 Selector 中被阻塞,则线程的中断状态将被设置,并且它将立即从选择操作返回,可能带有非零值,就像调用了选择器的唤醒方法一样。

如果前面的条件都不成立,则将设置该线程的中断状态。

注意最后一段;本质上,除非您的线程当前正在执行上述等待式操作之一,否则中断线程只会设置一个标志,但不会影响控制流。无论调用是来自不同的线程还是受害者线程本身,这都是正确的。

换句话说:

try {
   while (true) {
        queue.take();
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

// When an interrupt is received, the thread resumes
// execution here. It will notice the effect of the
// Thread.currentThread().interrupt() only, if it tries
// to perform any of the mentioned special wait operations
// here.

synchronized (someLockObject) {

    while (!someCondition) {

        // Will immediately terminate with an `InterruptedException`,
        // since we properly restored the interrupt status
        // above.

        someLockObject.wait();
    }
}

这种方法实现了当前调用堆栈中的所有代码都有机会识别中断,并执行必要的清理和“有序”关闭。如果您不恢复中断状态,那么try/后面的代码catch可能会错误地认为一切都很好,并且应该恢复正常执行。

于 2016-07-21T19:15:16.610 回答