在Java中处理InterruptedException
以下处理InterruptedException
方法有什么区别? 什么是最好的办法呢?
try{ //... } catch(InterruptedException e) { Thread.currentThread().interrupt(); }
要么
try{ //... } catch(InterruptedException e) { throw new RuntimeException(e); }
编辑:我想也知道在这两个使用的情况下。
你可能会问这个问题,因为你调用了一个抛出InterruptedException
的方法。
首先,你应该看到throws InterruptedException
它是什么:方法签名的一部分和调用你调用的方法可能的结果。 因此,首先接受InterruptedException
是方法调用完全有效的结果。
现在,如果你调用的方法抛出这样的exception, 你的方法应该做什么? 你可以通过考虑以下几点来找出答案:
你正在执行的方法抛出一个InterruptedException
是否有意义? 换句话说,调用你的方法时, InterruptedException
是一个明智的结果吗?
-
如果是的话 ,那么
throws InterruptedException
应该是你的方法签名的一部分,你应该让exception传播(即不要捕捉它)。示例 :您的方法等待networking中的值来完成计算并返回结果。 如果阻塞networking调用抛出
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
声明你的方法,你应该(必须!)捕获exception。 在这种情况下,现在需要牢记两件事情:-
有人中断了你的线程。 有人可能急于取消操作,优雅地终止程序,或其他任何事情。 你应该对这个人有礼貌,并且不要再从你的方法中回报。
-
即使你的方法可以设法产生一个合理的返回值的情况下,一个
InterruptedException
线程已被InterruptedException
的事实可能仍然是重要的。 尤其是,调用您的方法的代码可能会对执行您的方法期间是否发生中断感兴趣。 因此,应该通过设置中断标志来logging发生中断的事实: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)
是一个坏主意。 来电者不太礼貌。 你可以发明一个新的运行时exception,但根本原因(有人希望线程停止执行)可能会丢失。
其他例子:
实现
Runnable
:正如你可能已经发现的那样,Runnable.run
的签名不允许重新抛出InterruptedExceptions
。 那么, 你注册了实现Runnable
,这意味着你注册了处理可能的InterruptedExceptions
。 select一个不同的接口,如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; }
这篇文章在这里被重写为一篇文章。
就在这个时候,我正在阅读关于今天上午的这篇文章,这篇文章是在 Brian Goetz的Java Concurrency In Practice的工作中。 基本上他说你应该做两件事情之一
-
传播
InterruptedException
– 声明您的方法抛出检查的InterruptedException
以便您的调用者必须处理它。 -
恢复中断 – 有时你不能抛出
InterruptedException
。 在这些情况下,您应该捕获InterruptedException
并通过调用currentThread
上的interrupt()
方法来恢复中断状态,以便调用堆栈上方的代码可以看到发生了中断。
你想做什么?
InterruptedException
在一个线程正在等待或正在hibernate时被抛出,另一个线程使用Thread
类中的interrupt
方法中断它。 所以如果你发现这个exception,就意味着这个线程已经被中断。 通常调用Thread.currentThread().interrupt();
再次,除非你想检查从其他地方线程的“中断”状态。
关于抛出RuntimeException
的其他select,似乎并不是一个明智的做法(谁会捕获这个?它将如何被处理?),但是如果没有额外的信息就很难说清楚。
对我来说关键的是:InterruptedException并不是什么问题,它是做你所要做的事的线程。 因此,将它重新包装在RuntimeException中是毫无意义的。
在很多情况下,重新抛出包含在RuntimeException中的exception是很有意义的,当你说,我不知道这里出了什么问题,我不能做任何事情来解决它,我只是希望它离开当前的处理stream程并击中任何应用程序范围的exception处理程序,我可以logging它。 InterruptedException并不是这样,它只是响应有中断()的线程,而是抛出InterruptedException,以便及时取消线程的处理。
因此,传播InterruptedException,或者智能地进行处理(意思是在一个完成它意味着要做的事情的地方)并重置中断标志。 请注意,当InterruptedException被抛出时,中断标志被清除; Jdk库开发人员所做的假设是捕获exception数量来处理它,所以默认情况下该标志被清除。
所以绝对的第一种方法是更好的,在问题中的第二个发布的例子是没有用的,除非你不希望线程实际上被打断,并中断它相当于一个错误。
下面是我写的一个答案,用一个例子来描述中断是如何工作的 。 您可以在示例代码中看到它正在使用InterruptedException从Runnable的run方法的while循环中退出。
正确的默认select是添加InterruptedException到你的抛出列表。 中断表示另一个线程希望你的线程结束。 这个请求的原因并不明显,完全是上下文的,所以如果你没有任何额外的知识,你应该认为这只是一个友好的closures,任何避免closures的东西都是非友好的回应。
Java不会随机抛出InterruptedException,所有的build议不会影响你的应用程序,但是我遇到了一个情况,开发人员遵循“燕子”策略变得非常不方便。 一个团队开发了大量的testing,并使用Thread.Sleep很多。 现在我们开始在我们的CI服务器上运行testing,有时由于代码中的缺陷会陷入永久等待中。 为了使情况变得更糟,当试图取消CI作业时,它永远不会closures,因为打算中止testing的Thread.Interrupt不会中止作业。 我们必须login到该框并手动杀死进程。
所以长话短说,如果你只是抛出InterruptedException,那么你的线程应该结束默认的意图。 如果你不能把InterruptedException添加到你的throw列表中,我会把它包装在一个RuntimeException中。
有一个非常合理的理由,InterruptedException本身应该是一个RuntimeException,因为这会鼓励更好的“默认”处理。 这不是一个RuntimeException,只是因为devise师坚持一个RuntimeException应该代表您的代码中的错误的分类规则。 由于InterruptedException并不是直接来自你的代码中的错误,所以不是。 但事实上,经常会出现InterruptedException,因为在你的代码中有一个错误(例如死循环,死锁),中断是其他线程处理错误的方法。
如果你知道有合理的清理工作要做,那就去做吧。 如果你知道中断的深层原因,你可以采取更全面的处理。
所以总之你的select应该遵循这个清单:
- 默认情况下,添加到引发。
- 如果不允许添加抛出,则抛出RuntimeException(e)。 (多种不良select的最佳select)
- 只有当你知道中断的明确原因时,才能按照需要进行处理。 如果你的方法是局部的,那么调用Thread.currentThread()。interrupt()会中断重置。
我只是想增加最后一个选项,大多数人和文章提到。 正如mR_fr0g所说的那样,正确地处理中断很重要,或者通过:
-
传播InterruptException
-
线程上恢复中断状态
或者另外:
- 自定义处理中断
根据您的情况,以自定义的方式处理中断没有任何问题。 由于中断是终止请求,与强制命令相反,完成额外的工作以允许应用程序正常处理请求是完全有效的。 例如,如果线程处于睡眠状态,等待IO或硬件响应,则当它收到中断时,在终止线程之前正常closures任何连接是完全有效的。
我强烈build议理解这个话题,但这篇文章是一个很好的信息来源: http : //www.ibm.com/developerworks/java/library/j-jtp05236/
在某些情况下,我会说不做任何事情。 可能不是你应该做的默认情况下,但万一没有办法中断发生,我不知道还有什么要做(可能会logging错误,但不影响程序stream)。
一种情况是你有一个任务(阻塞)队列。 如果你有一个守护进程线程处理这些任务,并且你不会自己中断线程(就我所知,jvm不会中断jvmclosures的守护进程线程),我看不到中断发生的方式,因此它可能是只是忽略了。 (我知道守护线程可能随时被jvm杀死,因此在某些情况下是不合适的)。
编辑:另一种情况可能是防护块,至less基于Oracle的教程: http : //docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html