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向上传播调用堆栈,并throwsInterruptedException依次向每个方法中添加一个。这看起来似乎很笨拙,但这实际上是一个理想的属性-方法的签名现在向调用者表明它将对中断做出快速响应。
//如果不确定,让调用者确定如何处理中断 public void myLongRunningMethod() throws InterruptedException { ... }
在有限的情况下(例如,在覆盖没有throw任何检查异常的方法时),您可以在不引发异常的情况下重置中断状态,并期望接下来执行任何代码来处理中断。这会延迟处理中断,但不能完全消除中断。
//抑制异常,但重置中断状态,让以后的代码 //检测中断并正确处理。 try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return ...; //此时您的期望仍然无法实现-尽量不要做更多的工作。 }