Java:Thread.interrupted()和Thread.isInterrupted()之间的使用差异?

时间:2009-12-14 22:52:10

标签: java multithreading

Java问题:据我所知,有两种方法可以检查线程内部线程是否收到中​​断信号Thread.interrupted()Thread.isInterrupted(),它们之间的唯一区别是前者重置内部中断的旗帜。

到目前为止,我一直使用Thread.isInterrupted()并且从未遇到任何问题。然后,我见过的大多数教程都建议使用Thread.interrupted()。这有什么具体原因吗?

9 个答案:

答案 0 :(得分:79)

interrupted()static并检查当前线程。 isInterrupted()是一个实例方法,用于检查调用它的Thread对象。

常见错误是在实例上调用静态方法。

Thread myThread = ...;
if (myThread.interrupted()) {} // WRONG! This might not be checking myThread.
if (myThread.isInterrupted()) {} // Right!

另一个区别是interrupted()也会清除当前线程的状态。换句话说,如果你连续两次调用它并且线程没有在两次调用之间中断,那么第二次调用将返回false,即使第一次调用返回true

Javadocs告诉你这样重要的事情;经常使用它们!

答案 1 :(得分:34)

如果你使用interrupted,你问的是“自从我上次询问以来我是否被打断了?”

isInterrupted告诉您调用它的线程当前是否被中断。

答案 2 :(得分:7)

interrupted()方法是一种类方法,始终检查当前线程清除中断“标志”。换句话说,第二次调用interrupted()将返回false

isInterrupted()方法是一种实例方法;它报告调用它的线程的状态。此外,它不会清除中断标志。如果设置了该标志,则在调用此方法后它将保持设置状态。

答案 3 :(得分:6)

InterruptedException周围有许多习语,但问题是明确检查中断状态。

我的理解是isInterrupted(实例方法)应该很少使用 - 主要用于日志记录和调试等。它只给出了给定线程上的标志的快照,之后很快就会过时。

正常的习惯用法是检查interrupted(静态方法),如果你正在编写一个你想要在调用抛出的东西的某个点上可取消的任务由于睡眠或阻止I / O呼叫等原因导致InterruptedException。如果你看到标志设置,你应该尽可能快地停止当前计算,提前返回或抛出异常(可能是InterruptedException)。

举个例子,如果您的任务看起来像

void process(Things[] things) throws InterruptedException {
    for (Thing thing : things) {
        thing.twiddle(); // this call throws InterruptedException
    }
}
那么你不需要做任何其他事情;如果有人在您的主题上调用Thread.interrupt,则在当前或下一个twiddle来电期间,InterruptedException会被抛出并停止您的任务。

但是如果twiddle 抛出InterruptedException并且通常不能在中间被打断怎么办?假设每个此类调用需要100毫秒,但things.length可能为100.然后process可能会被阻止10秒,即使有人试图中断它,这在您的应用程序中可能是不可接受的。所以你可以明确地检查中断:

void process(Things[] things) {
    if (Thread.interrupted()) {
        return;
    }
    for (Thing thing : things) {
        thing.twiddle();
    }
}

在这里你可以看到为什么interrupted原子地检查和清除标志是很重要的:你用它来确认收到一条消息,有人礼貌地要求你尽快停止。 (在这种情况下,在请求的大约100ms内。)您还可以看到为什么这必须是一个静态方法,在当前线程上运行:它只在检查是否应该停止周围代码的上下文中才有意义。

当然,如果process的来电者假设它已经完成,那么简单return如此处所示会产生误导。所以你可能希望让process返回它完成处理的事情的数量,或者更合适的是抛出异常:

void process(Things[] things) throws InterruptedException {
    if (Thread.interrupted()) {
        throw new InterruptedException();
    }
    for (Thing thing : things) {
        thing.twiddle();
    }
}

在这种情况下,调用者会收到(已检查)异常,通知他们其他人要求在中间停止处理。通常调用者应该让异常抛出调用堆栈。

如果您无法停止当前任务但又需要知道阻止它的请求,例如为了缩短剩下的工作时间,您也可以重新打破自己:

void process(Things[] things) {
    boolean twiddleFully = true;
    if (twiddleFully && Thread.interrupted()) {
        twiddleFully = false;
        Thread.currentThread().interrupt();
    }
    for (Thing thing : things) {
        thing.twiddle(twiddleFully);
    }
}

在这里,我们可以更快地处理剩余的事情,但仍然完成循环,然后重新打开中断的标志,以便我们的调用者可以决定处理它。

答案 4 :(得分:4)

Java中的线程中断是建议性的。如果调用Thread.interrupt(),它将设置标志并取消任何未完成的IO任务(将抛出InterruptedException)。但是,由线程中执行的代码来处理这个问题。这样做称为实现线程中断策略。

但是,由于Thread的中断状态是共享的,因此任何此类处理都必须是线程安全的。如果你正在处理它,你不希望其他一些线程关闭并试图用中断的标志做一些事情。出于这个原因,Thread.interrupted()标志使这个原子化,所以当你想说:“如果这个线程被中断然后我将处理它。”通常这将涉及清理一些资源。一旦你完成后你应该传播中断的标志,这样调用者就可以处理它。你可以通过调用Thread.interrupt来做到这一点。

答案 5 :(得分:4)

以下是一些如何使用这些方法的示例:

  1. 如果您正在编写自己的线程池,则可能需要检查正在管理的其中一个线程上的中断状态。在这种情况下,您可以致电managedThread.isInterrupted()检查其中断状态。

  2. 如果您正在编写自己的InterruptedException处理程序,它们不会立即通过Thread.currentThread().interrupt()重新触发等效异常(例如,您的异常处理程序后可能有一个finally块),您可能需要检查您当前正在运行的线程是否已通过外部调用或InterruptedException中断。在这种情况下,您将检查Thread.interrupted()的布尔值以检查当前线程的状态。

  3. 第二种方法对我来说真的只对我有用,在这种情况下,我害怕有人在较低级别编写了一个异常食者,通过扩展,它也吃了一个InterruptedException。

答案 6 :(得分:2)

interrupted()方法是类线程的静态方法,检查当前线程并清除中断“flag”.i.e。第二次调用interrupted()将返回false。

isInterrupted()方法是一个实例方法;它报告调用它的线程的状态。它没有清除中断标志。

如果设置了该标志,则在调用此方法后它将保持设置状态。

Thread myThread = ...;
if (myThread.interrupted()) {} //error

Thread.interrupted()//right

if (myThread.isInterrupted()) {} // Right

答案 7 :(得分:1)

这是一个老问题,经历了答案,我觉得还有一些缺失的信息。这是我尝试填写那段缺失的信息。

从Java 5开始,通常只会间接处理Threads。事实上,java.util.Executor框架中产生的线程是在库方法中处理的。这些线程通常调用具有阻塞性质的实体,如Future.get()。 ie get()阻止直到结果可用。现在有一个get()的重载形式接受超时值并且调用该方法意味着该线程想要等待一段时间等于{的超时{1}}返回一个值,如果不是,则可以通过Future.cancel()取消该任务。因此,这些方法严重处理中断,因为一旦他们嗅到中断,他们也会抛出已检查的InterruptionException。因此调用者被迫处理InterruptionException。由于它们已经传播了传递中断状态的InterruptedException,因此阻塞方法也可以通过调用Thread.interrupt()来清除中断状态。否则,违反了InterruptedException的合同。

但是,如果你正在处理当前没有推荐的原始线程,那么在调用静态方法get ()时应该小心,因为如果你连续两次调用它并且线程没有被中断两次调用,即使第一次调用返回true,第二次调用也将返回false。

答案 8 :(得分:1)

为什么要打扰?

当您需要长时间运行的任务现在需要停止时,或者当您有需要关闭的守护程序以及其他示例时,使用Java中的中断线程很有用。

如何打扰

要中断,请在线程上调用interrupt()。这是一个合作过程,因此您的代码必须为此做好准备。像这样:

myThread.interrupt();

负责的代码

您的代码的责任是为任何干扰做好准备。我想说的很久了,只要您有一个长期运行的任务,就插入一些中断准备好的代码,如下所示:

while (... something long...) {

     ... do something long

     if (Thread.interrupted()) {
         ... stop doing what I'm doing...
     }
}

如何停止我在做什么?

您有几种选择:

  1. 如果您位于 Runnable.run()中,只需返回或退出循环并完成该方法即可。
  2. 您可能正处于代码深处的其他某种方法中。此时,该方法可能会抛出 InterruptedException ,因此您只需这样做(将标志清除)即可。
  3. 但是也许在您的代码深处,抛出 InterruptedException 没有意义。在这种情况下,您应该抛出其他异常,但是在此之前,您的线程再次中断,因此捕获的代码知道正在进行中断。这是一个示例:
private void someMethodDeepDown() {
    while (.. long running task .. ) {
          ... do lots of work ...

          if (Thread.interrupted()) {
             // oh no! an interrupt!
             Thread.currentThread().interrupt();
             throw new SomeOtherException();
          }
     }
}

现在,异常可以传播终止线程或被捕获的异常,但是接收代码有望注意到正在进行中断。

我应该使用isInterrupted()还是interrupted()

您应该首选 interrupted(),因为:

  1. 您的代码应重置中断标志,因为如果您不使用所使用的线程,则可能返回到中断状态导致问题的线程池(当然,这是线程池代码中的错误,您不会例如,如果您使用 Executors.newFixedThreadPool(),就不会得到这种行为。但是其他线程代码也可以拥有它。
  2. 正如另一个回答所述,中断标志的清除表明您已收到消息并正在采取措施。如果您将其保留为true,则一段时间后的呼叫者会认为您不会及时对其进行响应。

为什么要使用interrupt()为什么在我的代码中不添加其他标志?

中断是最好的中断机制,因为我们的代码可以为之做好准备。如果我们发现只是捕获并忽略了 InterruptException 的代码,或者没有在其主体中检查 interrupted()的代码,则可以纠正这些错误,并使我们的代码始终清晰可中断无需在代码中创建对非标准机制的神秘依赖。

不幸的是,约书亚·布洛克(Joshua Block)在他的着名著作《有效Java,第二版》中提出了相反的说法。但是我相信启用 interrupt()方法可以按预期工作会更好。

Future.cancel()尚未处理吗?

将来取消将从正在运行的队列中删除任务。如果您的任务已经在运行,它将不会停止它。因此, cancel()是一个与中断不同的概念。正如Javadocs所说:

尝试取消执行此任务。如果此尝试将失败 任务已经完成,已经被取消或者可能 不会因为其他原因而被取消。如果成功了,这个任务 调用cancel时尚未启动,则该任务永远不会运行。如果 任务已经开始,那么mayInterruptIfRunning参数 确定执行此任务的线程是否应该 试图停止任务而中断。 https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/concurrent/Future.html#cancel(boolean)

但是,如果打开mayInterruptIfRunning,则调用它会产生一个中断。