在Java中处理InterruptedException

时间:2010-10-20 09:21:24

标签: java multithreading exception-handling interrupted-exception

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

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

OR

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

编辑:我也想知道这两种情况在哪些情况下使用过。

7 个答案:

答案 0 :(得分:362)

  

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

你可能会问这个问题,因为你已经调用了抛出InterruptedException的方法。

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

现在,如果您正在调用的方法抛出此类异常,您的方法应该做什么?您可以通过考虑以下因素来找出答案:

实施的方法是否有意义抛出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;
    }
    
  • 如果没有,那么您不应该使用throws InterruptedException声明您的方法,并且您应该(必须!)捕获异常。在这种情况下,现在要记住两件事:

    1. 有人打断了你的话题。有人可能急于取消操作,优雅地终止程序,或者其他什么。你应该礼貌地对待那个人并且不用再费力地从你的方法回来。

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

    3.   

      示例:用户要求打印两个值的总和。如果无法计算总和,则打印“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之类的方法,完全可以说,“如果我被打断,我无法完成尝试阅读文件的操作”。换句话说,抛出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;
}

此帖子已被重写为文章here

答案 1 :(得分:85)

碰巧我今天早上在Brian Goetz的Java Concurrency In Practice工作途中正在阅读这篇文章。基本上他说你应该做两件事之一

  1. 宣传InterruptedException - 声明您的方法以抛出选中的InterruptedException,以便您的来电者必须处理它。

  2. 恢复中断 - 有时您无法抛出InterruptedException。在这些情况下,您应该捕获InterruptedException并通过调用interrupt()上的currentThread方法来恢复中断状态,以便调用堆栈上方的代码可以看到发出了中断。 / p>

答案 2 :(得分:17)

你想做什么?

当线程正在等待或休眠时,抛出InterruptedException,而另一个线程使用类interrupt中的Thread方法中断它。因此,如果您捕获此异常,则表示该线程已被中断。通常没有必要再次调用Thread.currentThread().interrupt();,除非你想从其他地方检查线程的“中断”状态。

关于抛出RuntimeException的另一种选择,这似乎不是一件非常明智的事情(谁会抓住这个?它将如何处理?)但如果没有其他信息,很难说清楚。

答案 3 :(得分:12)

对我而言,关键是:InterruptedException不是出错的,它是线程做你告诉它做的事情。因此,重新抛出它包含在RuntimeException中是没有意义的。

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

因此传播InterruptedException,或者智能地使用它(意味着它将完成它本来要做的地方)并重置中断标志。注意,当抛出InterruptedException时,中断标志被清除; Jdk库开发人员的假设是捕获异常等于处理它,因此默认情况下标志被清除。

所以肯定第一种方式更好,问题中第二个发布的例子没用,除非你不希望线程实际上被中断,并且中断它就等于错误。

这是我写的describing how interrupts work, with an example的答案。您可以在示例代码中看到它使用InterruptedException来保存Runnable的run方法中的while循环。

答案 4 :(得分:8)

正确的默认选择是将InterruptedException添加到您的throws列表中。中断表示另一个线程希望您的线程结束。这个请求的原因并不明显,完全是上下文的,所以如果你没有任何额外的知识,你应该假设它只是一个友好的关闭,任何避免关闭的都是一个非友好的响应。

Java不会随机抛出InterruptedException,所有建议都不会影响你的应用程序,但我遇到了一个开发人员遵循“吞下”策略变得非常不方便的情况。一个团队开发了大量测试并使用了Thread.Sleep。现在我们开始在CI服务器中运行测试,有时由于代码中的缺陷会陷入永久等待。为了使情况更糟,当尝试取消CI作业时,它从未关闭,因为旨在中止测试的Thread.Interrupt并未中止作业。我们必须登录该框并手动终止进程。

很长一段时间,如果您只是抛出InterruptedException,那么您将匹配线程应该结束的默认意图。如果你不能将InterruptedException添加到你的throw列表中,我会把它包装在RuntimeException中。

有一个非常合理的论据要说InterruptedException本身应该是RuntimeException,因为这会鼓励更好的“默认”处理。它不是RuntimeException,只是因为设计者坚持使用RuntimeException应该代表代码中的错误的分类规则。由于InterruptedException不会直接来自代码中的错误,因此不会。但实际情况是,通常会出现InterruptedException,因为代码中存在错误(即无限循环,死锁),而Interrupt是处理该错误的其他线程方法。

如果您知道要进行合理的清理,那就去做吧。如果你知道中断的更深层原因,你可以采取更全面的处理。

总而言之,您的处理选择应遵循以下列表:

  1. 默认情况下,添加到throws。
  2. 如果不允许添加到throws,则抛出RuntimeException(e)。 (多种不良选项的最佳选择)
  3. 只有当您知道中断的明确原因时,才能根据需要进行处理。如果你的处理是你的方法的本地处理,那么通过调用Thread.currentThread()。interrupt()来中断复位。

答案 5 :(得分:3)

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

  • 传播InterruptException

  • 恢复线程上的中断状态

或另外:

  • 自定义处理中断

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

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

答案 6 :(得分:0)

我会说在某些情况下可以做任何事情。可能不是默认情况下你应该做的事情,但是如果没有办法让中断发生,我不知道还能做什么(可能是记录错误,但这不会影响程序流)。

如果您有任务(阻塞)队列,则会出现一种情况。如果您有一个守护程序线程处理这些任务而您不自行中断线程(据我所知,jvm不会在jvm shutdown上中断守护程序线程),我认为没有办法让中断发生,因此它可能是只是被忽略了(我知道守护程序线程可能随时被jvm杀死,因此在某些情况下不适合)。

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