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

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

OR

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

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


当前回答

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

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

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

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

您正在实现的方法抛出InterruptedException是否有意义?换句话说,在调用方法时,InterruptedException是合理的结果吗?

If yes, then throws InterruptedException should be part of your method signature, and you should let the exception propagate (i.e. don't catch it at all). Example: Your method waits for a value from the network to finish the computation and return a result. If the blocking network call throws an InterruptedException your method can not finish computation in a normal way. You let the InterruptedException propagate. int computeSum(Server server) throws InterruptedException { // Any InterruptedException thrown below is propagated int a = server.getValueA(); int b = server.getValueB(); return a + b; } If no, then you should not declare your method with throws InterruptedException and you should (must!) catch the exception. Now two things are important to keep in mind in this situation: Someone interrupted your thread. That someone is probably eager to cancel the operation, terminate the program gracefully, or whatever. You should be polite to that someone and return from your method without further ado. Even though your method can manage to produce a sensible return value in case of an InterruptedException the fact that the thread has been interrupted may still be of importance. In particular, the code that calls your method may be interested in whether an interruption occurred during execution of your method. You should therefore log the fact an interruption took place by setting the interrupted flag: Thread.currentThread().interrupt() Example: The user has asked to print a sum of two values. Printing "Failed to compute sum" is acceptable if the sum can't be computed (and much better than letting the program crash with a stack trace due to an InterruptedException). In other words, it does not make sense to declare this method with 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"); } }

现在应该已经清楚了,仅仅抛出新的RuntimeException(e)是一个坏主意。这对打电话的人不太礼貌。您可以创建一个新的运行时异常,但可能会丢失根本原因(有人希望线程停止执行)。

其他的例子:

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

 

Calling Thread.sleep: You're attempting to read a file and the spec says you should try 10 times with 1 second in between. You call Thread.sleep(1000). So, you need to deal with InterruptedException. For a method such as tryToReadFile it makes perfect sense to say, "If I'm interrupted, I can't complete my action of trying to read the file". In other words, it makes perfect sense for the method to 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; }


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

其他回答

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

Propagate the InterruptedException - Declare your method to throw the checked InterruptedException so that your caller has to deal with it. Restore the Interrupt - Sometimes you cannot throw InterruptedException. In these cases you should catch the InterruptedException and restore the interrupt status by calling the interrupt() method on the currentThread so the code higher up the call stack can see that an interrupt was issued, and quickly return from the method. Note: this is only applicable when your method has "try" or "best effort" semantics, i. e. nothing critical would happen if the method doesn't accomplish its goal. For example, log() or sendMetric() may be such method, or boolean tryTransferMoney(), but not void transferMoney(). See here for more details. Ignore the interruption within method, but restore the status upon exit - e. g. via Guava's Uninterruptibles. Uninterruptibles take over the boilerplate code like in the Noncancelable Task example in JCIP § 7.1.3.

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

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.

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

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

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

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

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

In many cases it makes sense to rethrow an exception wrapped in a RuntimeException when you say, I don't know what went wrong here and I can't do anything to fix it, I just want it to get out of the current processing flow and hit whatever application-wide exception handler I have so it can log it. That's not the case with an InterruptedException, it's just the thread responding to having interrupt() called on it, it's throwing the InterruptedException in order to help cancel the thread's processing in a timely way.

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

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

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

你想做什么?

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

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

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

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

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