在Java中处理InterruptedException

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

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

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

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

239632 次浏览

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

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

首先,你应该看到throws 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的情况下设法产生一个合理的返回值,线程被中断的事实可能仍然很重要。特别是,调用方法的代码可能对方法执行期间是否发生中断感兴趣。因此,您应该通过设置interrupt标志: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:你试图读取一个文件,规范说你应该在1秒之间尝试10次。调用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;
}

这篇文章已经被重写为文章在这里

你想做什么?

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

关于抛出RuntimeException的其他选项,这似乎不是一件非常明智的事情(谁会捕捉到这个?如何处理?)但如果没有额外的信息,很难透露更多信息。

碰巧我今天早上在上班的路上读到了Brian Goetz的Java并发实践。基本上他说你应该做三件事中的一件

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

  2. 恢复中断 -有时不能抛出InterruptedException。在这些情况下,你应该捕获InterruptedException并通过调用currentThread上的interrupt()方法来恢复中断状态,这样调用堆栈中较高的代码就可以看到中断已经发出,并快速从该方法返回。注意:这只适用于当你的方法有“尝试”或“尽最大努力”语义时,也就是说,如果方法没有完成它的目标,什么重要的事情都不会发生。例如,log()sendMetric()可能是这样的方法,或者boolean tryTransferMoney(),但不是void transferMoney()。详见here

  3. 忽略方法中的中断,但在退出时恢复状态 -例如通过番石榴的UninterruptiblesUninterruptibles接管了JCIP§7.1.3中不可取消任务示例中的样板代码。

对我来说,关键的事情是:一个InterruptedException并不是什么出错了,它是线程在做你告诉它做的事情。因此,在RuntimeException中重新抛出它毫无意义。

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

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

所以第一种方法肯定更好,问题中的第二个例子是没有用的,除非你不期望线程实际上被中断,而中断它等于一个错误。

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

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

Java不会随机抛出InterruptedException,所有的建议都不会影响你的应用程序,但我遇到过一个案例,开发人员遵循“吞咽”策略变得非常不方便。一个团队开发了大量的测试,并使用了Thread。多睡觉。现在我们开始在CI服务器中运行测试,有时由于代码中的缺陷会陷入永久等待。更糟糕的是,当试图取消CI作业时,它从未关闭,因为线程。打算中止测试的中断没有中止作业。我们必须登录到计算机并手动终止进程。

长话短说,如果您只是抛出InterruptedException,那么您就匹配了线程应该结束的默认意图。如果不能将InterruptedException添加到抛出列表中,我会将其包装在RuntimeException中。

有一个非常合理的论点是,InterruptedException本身应该是一个RuntimeException,因为这将鼓励更好的“默认”处理。它不是RuntimeException,只是因为设计者坚持了一个分类规则,即RuntimeException应该表示代码中的错误。因为InterruptedException不是直接由代码中的错误引起的,所以它不是。但实际情况是,InterruptedException通常是因为你的代码中出现了错误(即无休止的循环,死锁),而Interrupt是其他线程处理该错误的方法。

如果你知道有合理的清理工作要做,那就去做。如果你知道一个更深层次的中断原因,你可以采取更全面的处理。

因此,总的来说,你的处理方式应该遵循以下列表:

  1. 默认情况下,添加到抛出。
  2. 如果不允许添加到抛出,则抛出RuntimeException(e)。(多个坏选项中的最佳选择)
  3. 只有当您知道中断的显式原因时,才能按需要处理。如果你的处理是在你的方法的本地,那么重置中断调用Thread.currentThread().interrupt()。

我只是想给大多数人和文章提到的最后一个选项。正如mR_fr0g所述,正确地处理中断是很重要的:

  • 传播InterruptException

  • 恢复线程中断状态

或者另外:

  • 中断的自定义处理

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

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

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

一种情况是你有一个任务(阻塞)队列。如果你有一个守护线程处理这些任务,你自己不中断线程(据我所知,jvm不会中断守护线程在jvm关机),我看不到中断发生的方式,因此它可以被忽略。(我知道守护线程可能在任何时候被jvm杀死,因此在某些情况下是不合适的)。

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