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

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


OR

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


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

评论

javaspecialists.eu/archive/Issue056.html

#1 楼


以下处理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声明方法,应该(必须!)捕捉异常。现在,在这种情况下要记住两件事:


有人中断了您的线程。有人可能想取消该操作,优雅地终止该程序等。您应该对那个人彬彬有礼,并毫不费力地从您的方法中返回。
即使在InterruptedException的情况下您的方法可以设法产生合理的返回值,线程被中断的事实可能仍然很重要。特别是,调用您的方法的代码可能会对在执行方法期间是否发生中断感兴趣。因此,您应该通过设置中断标志来记录发生中断的事实:Thread.currentThread().interrupt()


示例:用户要求打印两个值的总和。如果无法计算总和,则打印“ Failed to compute sum”是可以接受的(并且比由于InterruptedException而使程序因堆栈跟踪而崩溃更好)。换句话说,用throws InterruptedException声明此方法是没有意义的。 throw new RuntimeException(e)是个坏主意。呼叫者不太礼貌。您可以发明一个新的运行时异常,但根本原因(有人希望线程停止执行)可能会丢失。

其他示例:


实现Runnable:您可能已经发现,Runnable.run的签名不允许重新抛出InterruptedExceptions。好吧,您已签署实现Runnable的实现,这意味着您已注册以应对可能的InterruptedExceptions。请选择其他接口,例如Callable,或按照上面的第二种方法。





调用Thread.sleep:您正在尝试阅读一个文件,规范说您应该尝试10次,中间间隔1秒。您致电Thread.sleep(1000)。因此,您需要处理InterruptedException。对于诸如tryToReadFile之类的方法,说“如果我被打断了,我将无法完成尝试读取文件的动作”是很有意义的。换句话说,对于抛出InterruptedExceptions的方法来说,这是非常有意义的。

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");
     }
}





本文已被重写为本文。

评论


第二种方法有什么问题?

–blitzkriegz
10-10-20在10:18

本文说您应该调用interrupt()来保留中断状态。不对Thread.sleep()执行此操作的原因是什么?

–oksayt
2011-2-8在7:54

我不同意,在您的线程不支持中断的情况下,收到的任何中断都表明出现了意外情况(即,编程错误,不良的API使用),并且使用RuntimeException进行应急是一个适当的响应(快速失败) )。除非被认为是线程总协定的一部分,否则即使您不支持中断,您也必须在收到中断时继续操作。

–amoe
13年8月30日在10:14



调用引发InterruptedExceptions并说您“不支持中断”的方法似乎像草率的编程和等待发生的错误。换句话说。如果您调用一个声明为抛出InterruptedException的方法,则该方法实际上确实会抛出此类异常,这不应是意外情况。

– aioobe
2015年9月7日在8:09



@MartiNito,不,在InterruptedException catch块中调用Thread.currentThread.interrupt()只会设置中断标志。它不会导致另一个InterruptedException被抛出/捕获在外部InterruptedException catch块中。

– aioobe
19年5月13日在18:24

#2 楼

碰巧的是,我今天早上刚刚读了Brian Goetz撰写的Java Concurrency In Practice的工作方式。基本上他说您应该做以下三件事之一


传播InterruptedException-声明您的方法来抛出已检查的InterruptedException,以便您的呼叫者必须对其进行处理。
还原中断-有时您无法抛出InterruptedException。在这些情况下,您应该捕获InterruptedException并通过调用interrupt()上的currentThread方法来恢复中断状态,以便调用堆栈上方的代码可以看到已发出中断,并迅速从该方法返回。注意:这仅在您的方法具有“尝试”或“尽力而为”语义时才适用。 e。如果该方法无法实现其目标,则不会发生任何严重的问题。例如,log()sendMetric()可能是这种方法,或者boolean tryTransferMoney(),但不是void transferMoney()。有关更多详细信息,请参见此处。

忽略方法内的中断,但在退出时恢复状态-e。 G。通过番石榴的UninterruptiblesUninterruptibles接管样板代码,如JCIP第7.1.3节中的“不可取消任务”示例中一样。


评论


“有时您不能抛出InterruptedException” –我会说,有时该方法不适合宣传InterruptedExceptions。您的表述似乎建议您应尽可能地抛出InterruptedException。

– aioobe
2014年10月9日,11:50

而且,如果您要阅读第7章,将会看到一些细微之处,如清单7.7所示,其中不可取消的任务不会立即恢复中断,而只是在完成后才恢复。格茨(Goetz)也有很多共同作者。

–嘶嘶声
2015年2月17日在0:20



我喜欢您的答案,因为它简洁明了,但是我认为在第二种情况下,它也应该说明从您的函数中快速轻松地返回。想象一个长(或无限)循环,中间有一个Thread.sleep()。捕获InterruptedException应该调用Thread.currentThread()。interrupt()并退出循环。

–扬·沃(Yann Vo)
18年8月22日在6:57

@YannVo我已编辑答案以应用您的建议。

– Leventov
19-10-17在18:49

#3 楼



当线程正在等待或进入睡眠状态时,将抛出InterruptedException,而另一个线程使用类interrupt中的Thread方法将其中断。因此,如果捕获到此异常,则意味着线程已被中断。通常,再次调用Thread.currentThread().interrupt();是没有意义的,除非您要从其他位置检查线程的“中断”状态。

关于抛出RuntimeException的其他选择,它看起来不是很明智的做法(谁会抓到这个?如何处理?),但是如果没有其他信息,很难告诉更多。

评论


再次调用Thread.currentThread()。interrupt()会设置中断标志,如果我们想确保在更高级别上注意到并处理中断,这确实很有用。

–彼得·托克(PéterTörök)
2010-10-20 9:31

@Péter:我只是在更新答案以提及这一点。谢谢。

–Grodriguez
2010-10-20 9:32

#4 楼

对我来说,关键的是:InterruptedException没什么错,它是线程按照您的指示执行的。因此,将其重新包装在RuntimeException中具有零意义。

在很多情况下,当您说“我不知道这里出了什么问题,我无能为力,只能解决它”时,抛出一个包装在RuntimeException中的异常是有意义的为了摆脱当前的处理流程并击中我拥有的所有应用程序范围的异常处理程序,以便可以对其进行记录。使用InterruptedException并不是这种情况,它只是响应调用了interrupt()的线程,它抛出InterruptedException以帮助及时取消线程的处理。

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

因此,肯定第一种方法更好,问题中的第二个示例无济于事,除非您不希望线程实际被中断,并且中断它会导致错误。

这是我写的一个答案,其中举例说明了中断的工作方式。您可以在示例代码中看到它使用InterruptedException来摆脱Runnable的run方法中的while循环的麻烦。

#5 楼

正确的默认选择是将InterruptedException添加到您的引发列表。中断表示另一个线程希望您的线程结束。提出此请求的原因并不明显,并且完全是上下文相关的,因此,如果您没有任何其他知识,则应假定它只是友好的关闭,而避免关闭的任何操作都是不友好的响应。

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

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

有一个非常合理的论点,即InterruptedException应该本身就是RuntimeException,因为这会鼓励更好的“默认”处理。这不是RuntimeException的唯一原因是设计人员坚持使用分类规则,即RuntimeException应该代表代码中的错误。由于InterruptedException并非直接源于代码中的错误,因此并非如此。但是现实情况是,由于代码中存在错误(即无限循环,死锁),经常会出现InterruptedException,而Interrupt是处理该错误的其他一些线程的方法。

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

因此,总的来说,您的处理选择应遵循以下列表:


默认情况下,添加到throws。
如果不允许添加到throws,则抛出RuntimeException(e)。 (多个错误选项的最佳选择)

只有当您知道明显的中断原因时,才按需进行处理。如果您的处理是方法的本地操作,请通过调用Thread.currentThread()。interrupt()来中断中断。


#6 楼

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


传播InterruptException
在线程上恢复中断状态

或另外:


自定义处理中断

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

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

#7 楼

我会说在某些情况下什么都不做是可以的。默认情况下,可能不是您应该执行的操作,但是如果万一没有办法发生中断,则我不确定要执行其他操作(可能记录错误,但这不会影响程序流)。

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

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

评论


这是个坏建议。在考虑任何重要的任务以至于忽略中断时,我都会遇到极大的麻烦。 Oracle可能只是懒惰,不想对Thread.sleep()调用增加(更多)混乱。除非您知道线程被中断的原因,否则您应该停止正在执行的任何操作(返回或抛出异常以帮助您的线程尽快死亡)。

–阿贾克斯
16年1月28日,下午3:33

这就是为什么我有时说。同样是因为还没有建议使用它,在我看来,在某些情况下还可以。我猜这取决于您正在构建哪种软件,但是如果您有一个永远不应该停止的24/7工作线程(除了JVM关闭),我将处理例如将来自BlockingQueue的项目作为错误(即日志),因为“不应发生”,然后重试。当然,我将有单独的标志来检查程序终止。在我的某些程序中,在正常操作下不应该关闭JVM。

– BjarneBoström
16年1月28日在11:36

或换种说法:在我看来,冒一个额外的错误日志语句(例如,在错误日志上发送邮件)的风险要比由于中断异常而应运行24/7的应用程序停止运行要好,为此,我认为在正常情况下不会发生任何事情。

– BjarneBoström
16 Jan 28'在11:47



嗯,您的用例可能有所不同,但是在理想的情况下,您不只是因为被打扰而终止。您停止将工作从队列中移开,让该线程死亡。如果线程的调度程序也被中断并被告知关闭,那么JVM将终止,并且其游戏结束。特别是,如果您发送kill -9或试图关闭您的邮件服务器或其他设备,则它将忽略您,直到您强制退出为止,从而防止正常关闭代码的其他部分。在写入磁盘或数据库时强行杀死,我敢肯​​定,将会发生美好的事情。

–阿贾克斯
16 Jan 30'15:29