398

以下处理方式有什么区别InterruptedException?最好的方法是什么?

try{
 //...
} catch(InterruptedException e) { 
   Thread.currentThread().interrupt(); 
}

或者

try{
 //...
} catch(InterruptedException e) {
   throw new RuntimeException(e);
}

编辑:我还想知道这两个在哪些场景中使用。

4

7 回答 7

532

以下处理InterruptedException的方式有什么区别?最好的方法是什么?

你可能会问这个问题,因为你调用了一个 throws 的方法InterruptedException

首先,您应该throws InterruptedException了解它是什么:方法签名的一部分以及调用您正在调用的方法的可能结果。因此,首先要接受这样一个事实,即 anInterruptedException是方法调用的完全有效的结果。

现在,如果你调用的方法抛出了这样的异常,你的方法应该怎么做?您可以通过思考以下问题来找出答案:

正在实施的方法抛出一个 是否有意义InterruptedException换句话说,InterruptedException调用你的方法是一个明智的结果吗?

  • 如果,那么throws InterruptedException应该是您的方法签名的一部分,并且您应该让异常传播(即根本不捕获它)。

    示例:您的方法等待来自网络的值以完成计算并返回结果。如果阻塞网络调用抛出一个InterruptedException你的方法不能以正常方式完成计算。你让InterruptedException传播。

    int computeSum(Server server) throws InterruptedException {
        // Any InterruptedException thrown below is propagated
        int a = server.getValueA();
        int b = server.getValueB();
        return a + b;
    }
    
  • 如果no,那么你不应该声明你的方法,throws InterruptedException你应该(必须!)捕获异常。现在,在这种情况下要记住两件事很重要:

    1. 有人打断了你的话题。有人可能急于取消操作,优雅地终止程序,或其他任何事情。你应该对那个人有礼貌,然后毫不犹豫地从你的方法中返回。

    2. 即使您的InterruptedException方法可以设法在线程已被中断的情况下产生合理的返回值,这仍然很重要。特别是,调用您的方法的代码可能对您的方法执行期间是否发生中断感兴趣。因此,您应该通过设置中断标志来记录发生中断的事实:Thread.currentThread().interrupt()

    示例:用户要求打印两个值的总和。Failed to compute sum如果无法计算总和,则打印 " " 是可以接受的(并且比让程序因 的堆栈跟踪而崩溃要好得多InterruptedException)。换句话说,用 .声明这个方法是没有throws InterruptedException意义的。

    void printSum(Server server) {
         try {
             int sum = computeSum(server);
             System.out.println("Sum: " + sum);
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();  // set interrupt flag
             System.out.println("Failed to compute sum");
         }
    }
    

现在应该很清楚,只是做throw new RuntimeException(e)是一个坏主意。这对来电者不太礼貌。您可以发明一个新的运行时异常,但根本原因(有人希望线程停止执行)可能会丢失。

其他示例:

实施Runnable:您可能已经发现, 的签名Runnable.run不允许重新抛出InterruptedExceptions。好吧,签了执行Runnable,这意味着签了处理可能InterruptedExceptions。要么选择不同的界面,例如Callable,要么遵循上面的第二种方法。

 

调用Thread.sleep:您正在尝试读取文件,并且规范说您应该尝试 10 次,中间间隔 1 秒。你打电话Thread.sleep(1000)。所以,你需要处理InterruptedException. 对于诸如“如果我被打断,我将无法完成尝试读取文件的操作”tryToReadFile这样的方法非常有意义。换句话说, throw 方法非常有意义。InterruptedExceptions

String tryToReadFile(File f) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        if (f.exists())
            return readFile(f);
        Thread.sleep(1000);
    }
    return null;
}

这篇文章在这里被改写为一篇文章。

于 2010-10-20T09:24:52.117 回答
116

碰巧的是,我今天早上在前往Brian Goetz的Java Concurrency In Practice工作的路上读到了这篇文章。基本上他说你应该做三件事之一

  1. 传播InterruptedException- 声明你的方法抛出检查InterruptedException,以便你的调用者必须处理它。

  2. 恢复中断- 有时你不能扔InterruptedException。在这些情况下,您应该通过调用 上的方法来捕获InterruptedException并恢复中断状态,以便调用堆栈更高的代码可以看到发出了中断,并快速从该方法返回。注意:这仅适用于您的方法具有“尝试”或“尽力而为”语义的情况,即如果该方法没有实现其目标,则不会发生任何严重的事情。例如,可能是这样的方法,或,但不是有关更多详细信息,请参见此处interrupt()currentThreadlog()sendMetric()boolean tryTransferMoney()void transferMoney()

  3. 忽略方法内的中断,但在退出时恢复状态- 例如通过 Guava 的Uninterruptibles. Uninterruptibles接管样板代码,如 JCIP § 7.1.3 中的不可取消任务示例。
于 2010-10-20T09:45:23.877 回答
24

你想做什么?

InterruptedException一个线程正在等待或休眠并且另一个线程使用interruptclass 中的方法中断它时抛出Thread。所以如果你捕捉到这个异常,就意味着线程已经被中断了。通常再次调用是没有意义的Thread.currentThread().interrupt();,除非您想从其他地方检查线程的“中断”状态。

关于你抛出 a 的其他选择RuntimeException,这似乎不是一件非常明智的事情(谁会抓住这个?如何处理?)但如果没有额外的信息,很难说出更多信息。

于 2010-10-20T09:25:11.973 回答
12

The correct default choice is add InterruptedException to your throws list. An Interrupt indicates that another thread wishes your thread to end. The reason for this request is not made evident and is entirely contextual, so if you don't have any additional knowledge you should assume it's just a friendly shutdown, and anything that avoids that shutdown is a non-friendly response.

Java will not randomly throw InterruptedException's, all advice will not affect your application but I have run into a case where developer's following the "swallow" strategy became very inconvenient. A team had developed a large set of tests and used Thread.Sleep a lot. Now we started to run the tests in our CI server, and sometimes due to defects in the code would get stuck into permanent waits. To make the situation worse, when attempting to cancel the CI job it never closed because the Thread.Interrupt that was intended to abort the test did not abort the job. We had to login to the box and manually kill the processes.

So long story short, if you simply throw the InterruptedException you are matching the default intent that your thread should end. If you can't add InterruptedException to your throw list, I'd wrap it in a RuntimeException.

There is a very rational argument to be made that InterruptedException should be a RuntimeException itself, since that would encourage a better "default" handling. It's not a RuntimeException only because the designers stuck to a categorical rule that a RuntimeException should represent an error in your code. Since an InterruptedException does not arise directly from an error in your code, it's not. But the reality is that often an InterruptedException arises because there is an error in your code, (i.e. endless loop, dead-lock), and the Interrupt is some other thread's method for dealing with that error.

If you know there is rational cleanup to be done, then do it. If you know a deeper cause for the Interrupt, you can take on more comprehensive handling.

So in summary your choices for handling should follow this list:

  1. By default, add to throws.
  2. If not allowed to add to throws, throw RuntimeException(e). (Best choice of multiple bad options)
  3. Only when you know an explicit cause of the Interrupt, handle as desired. If your handling is local to your method, then reset interrupted by a call to Thread.currentThread().interrupt().
于 2013-09-20T21:32:50.953 回答
12

对我来说,关键是:InterruptedException 没有任何问题,它是线程按照您的要求执行的操作。因此,重新抛出它包装在 RuntimeException 中是零意义的。

在许多情况下,当您说,我不知道这里出了什么问题并且我无法修复它时,重新抛出一个包装在 RuntimeException 中的异常是有意义的,我只是希望它脱离当前的处理流程并点击我拥有的任何应用程序范围的异常处理程序,以便它可以记录它。InterruptedException 不是这种情况,它只是响应调用它的 interrupt() 的线程,它抛出 InterruptedException 以帮助及时取消线程的处理。

所以传播 InterruptedException,或者聪明地吃掉它(意味着在一个它已经完成它应该做的事情的地方)并重置中断标志。请注意,当抛出 InterruptedException 时,中断标志会被清除;Jdk 库开发人员所做的假设是捕获异常相当于处理它,因此默认情况下该标志被清除。

所以肯定第一种方法更好,问题中的第二个发布示例没有用,除非您不希望线程实际被中断,并且中断它会导致错误。

这是我写的一个答案,它用一个例子来描述中断是如何工作的。您可以在示例代码中看到它使用 InterruptedException 来摆脱 Runnable 的 run 方法中的 while 循环。

于 2011-08-03T07:10:04.290 回答
5

我只是想在大多数人和文章提到的内容中添加最后一个选项。正如 mR_fr0g 所说,通过以下方式正确处理中断很重要:

  • 传播中断异常

  • 恢复线程上的中断状态

或者另外:

  • 中断的自定义处理

根据您的情况,以自定义方式处理中断没有任何问题。由于中断是终止请求,而不是强制命令,因此完成额外的工作以允许应用程序优雅地处理请求是完全有效的。例如,如果一个线程正在睡眠,等待 IO 或硬件响应,当它收到中断时,那么在终止线程之前优雅地关闭任何连接是完全有效的。

我强烈建议您了解该主题,但这篇文章是一个很好的信息来源:http: //www.ibm.com/developerworks/java/library/j-jtp05236/

于 2013-12-17T02:38:10.953 回答
0

我会说在某些情况下什么都不做也没关系。默认情况下可能不是您应该做的事情,但如果中断不应该发生,我不确定还能做什么(可能会记录错误,但这不会影响程序流程)。

一种情况是您有一个任务(阻塞)队列。如果您有一个守护线程处理这些任务并且您不自己中断线程(据我所知,jvm 不会在 jvm 关闭时中断守护线程),我认为中断不会发生,因此它可能是只是忽略了。(我确实知道守护线程可能随时被 jvm 杀死,因此在某些情况下不适合)。

编辑:另一种情况可能是受保护的块,至少基于 Oracle 的教程: http ://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

于 2015-02-25T13:58:38.497 回答