处理 Java 中的中断异常

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

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

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

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


答案 1

以下处理中断异常的方法有什么区别?最好的方法是什么?

您可能来问这个问题,因为您调用了一个抛出 .InterruptedException

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

现在,如果您调用的方法引发此类异常,您的方法应该怎么做?您可以通过考虑以下几点来找出答案:

您正在实现的方法抛出中断异常是否有意义?换句话说,调用方法时是一个明智的结果吗?InterruptedException

  • 如果,那么应该是方法签名的一部分,并且应该让异常传播(即根本不捕获它)。throws InterruptedException

    示例:您的方法等待来自网络的值完成计算并返回结果。如果阻塞网络调用引发您的方法无法以正常方式完成计算。你让传播。InterruptedExceptionInterruptedException

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

    示例:用户要求打印两个值的总和。如果无法计算总和,则打印“”是可以接受的(并且比由于 ) 而让程序崩溃并伴有堆栈跟踪要好得多。换句话说,使用 声明此方法是没有意义的。Failed to compute sumInterruptedExceptionthrows 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:您可能已经发现,的签名不允许重新抛出 。好吧,注册了实施,这意味着注册以处理可能的.选择其他接口(如 Callable),或遵循上述第二种方法。Runnable.runInterruptedExceptionsRunnableInterruptedExceptions

 

调用 Thread.sleep:您正在尝试读取文件,规范说您应该尝试 10 次,中间间隔 1 秒。您呼叫 .因此,您需要处理.对于像这样的方法来说,说“如果我被打断,我无法完成尝试读取文件的操作”是完全有意义的。换句话说,该方法投掷是完全有意义的。Thread.sleep(1000)InterruptedExceptiontryToReadFileInterruptedExceptions

String tryToReadFile(File f) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        if (f.exists())
            return readFile(f);
        Thread.sleep(1000);
    }
    return null;
}

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


答案 2

碰巧的是,我今天早上在去Java Concurrency In Practice工作的路上读到了Brian Goetz。基本上,他说你应该做三件事之一。

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

  2. 恢复中断 - 有时您无法抛出 .在这些情况下,您应该通过调用 上的方法来捕获并恢复中断状态,以便调用堆栈上方的代码可以看到已发出中断,并快速从该方法返回。注意:这仅适用于您的方法具有“尝试”或“尽力而为”的语义,即如果方法没有实现其目标,则不会发生任何关键情况。例如,log()sendMetric() 可能是这样的方法,或者布尔 tryTransferMoney(),但不是 void transferMoney()。有关更多详细信息,请参阅此处InterruptedExceptionInterruptedExceptioninterrupt()currentThread

  3. 忽略方法中的中断,但在退出时恢复状态 - 例如通过Guava的不间断物。 接管样板代码,如 JCIP § 7.1.3 中的不可禁用任务示例中所示。Uninterruptibles

推荐