Java处理InterruptedException

示例

InterruptedException是一个令人困惑的野兽-它以看似无害的方法(例如)出现,但处理不当会导致难以管理的代码在并发环境中表现不佳。Thread.sleep()

从最基本的意义上说,如果InterruptedException捕获到,则意味着有人在某个地方在您的代码当前正在运行的线程上调用它。您可能倾向于说“这是我的代码!我永远不会中断它!” 因此,请执行以下操作:Thread.interrupt()

//坏。不要这样
try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  // 漠视
}

但这恰恰是处理“不可能”事件发生的错误方法。如果您知道您的应用程序永远不会遇到,InterruptedException则应将此类事件视为严重违反程序假设的事件,并尽快退出。

处理“不可能的”中断的正确方法如下:

// 什么都不会打断您的代码
try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  Thread.currentThread().interrupt();
  throw new AssertionError(e);
}

这有两件事;它首先恢复线程的中断状态(就像InterruptedException没有首先抛出该线程一样),然后抛出一个AssertionError指示,表明您的应用程序的基本不变式已被违反。如果您确定永远不会中断该线程,则在此代码中运行此代码是安全的,因为catch永远不会到达该块。

使用番石榴的Uninterruptibles课程有助于简化这种模式。调用会忽略线程的中断状态,直到睡眠持续时间到期为止(此时该状态将恢复,以供以后的调用检查并抛出自己的中断)。如果您知道您永远不会中断此类代码,则可以安全地避免将睡眠调用包装在try-catch块中。Uninterruptibles.sleepUninterruptibly()InterruptedException

但是,您更经常不能保证线程永远不会中断。特别是,如果您正在编写将由一个Executor或其他线程管理执行的代码,则代码对中断迅速做出响应至关重要,否则应用程序将停滞甚至死锁。

在这种情况下,最好的做法通常是允许InterruptedException向上传播调用堆栈,并throws InterruptedException依次向每个方法中添加一个。这看起来似乎很笨拙,但这实际上是一个理想的属性-方法的签名现在向调用者表明它将对中断做出快速响应。

// 如果不确定,让调用者确定如何处理中断
public void myLongRunningMethod() throws InterruptedException {
  ...
}

在有限的情况下(例如,在覆盖没有throw任何检查异常的方法时),您可以在不引发异常的情况下重置中断状态,并期望接下来执行任何代码来处理中断。这会延迟处理中断,但不能完全消除中断。

// 抑制异常,但重置中断状态,让以后的代码
// 检测中断并正确处理。
try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  Thread.currentThread().interrupt();
  return ...; // 此时您的期望仍然无法实现-尽量不要做更多的工作。
}