另一个
Thread
在
InterruptedException
的情况下没有加入(join)你的
Thread
。
join()
应该永远等待或者直到调用
Thread
被中断。
首先,您需要考虑是否实际上会发生
InterruptedException
。有几种处理它的可能性。您可以:
吞下它。警告:只有在您确定InterruptedException
永远不会发生时才这样做。(你确定吗?真的吗?)
再次设置中断标志。如果您的代码不知道如何处理它,但线程还在做其他事情,也许对InterruptedException
感兴趣,那么就这样做。
传播它。如果当前方法不知道如何处理它,但是可能调用者知道,则这是最佳解决方案。
处理它。也许您当前的Thread
只是想在接收到InterruptedException
时停止(通过完成其run()
方法)。
将其包装在另一个异常中。有两个变体。
- 如果
InterruptedException
本来不应该发生并且明显是错误,则可以将其包装在断言中。
- 如果您的方法绑定到不允许
InterruptedException
的特定合同,则可以将其包装在另一个异常中。
无论哪种情况,我强烈建议以某种方式记录
InterruptedException
。如果您没有记录它,并且它发生了,程序的行为可能很难理解。
吞下它
警告:只有在您绝对确定这样做没问题的情况下才能吞下它。你已经被警告过了。
public static void joinThread(final Thread thread) {
while (true)
try {
thread.join();
return;
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.ERROR, "Unexpected InterruptedException", e);
}
}
作为这个问题的一个变体,您可以选择忽略它而不进行重试。
public static void joinThread(final Thread thread) {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.ERROR, "Unexpected InterruptedException", e);
}
}
再次设置interrupted
标志。
如果Thread
还在做其他事情,而您的代码不知道如何处理InterruptedException
,但其他代码可以处理,则需要执行此操作。
public static void joinThread(final Thread thread) {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.INFO, "Unexpected InterruptedException, resetting flag", e);
Thread.currentThread().interrupt();
}
}
< p > < em > 但是请注意! 这只有在其他代码真正知道该做什么时才是一个好主意。如果其他代码执行相同的操作,并且该操作在循环中(这通常是
Thread
的情况),那么您刚刚将好的阻塞代码转换为忙等待垃圾,因为
interrupted
标志永远不会被正确清除。虽然这种解决方案在许多博客文章中被宣传为最佳或真正的解决方案,但只要没有任何能够处理
InterruptedException
并实际保持标志已清除而不是重置它的代码,就是无意义的。
传播它
这是最佳解决方案,如果该方法本身不知道如何处理它,但调用者可能知道。
public static void joinThread(final Thread thread) throws InterruptedException {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.FINE, "Got InterruptedException, propagating it to the caller", e);
throw e;
}
}
如果调用者对其进行了良好的处理,您可以删除不必要的部分,如日志记录和重新抛出。
处理它。
如果方法本身知道在被中断时必须执行某些操作,它可以简单地捕获异常并执行预期的操作。
public static void joinThread(final Thread thread) {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.FINE, "Got InterruptedException, handling it", e);
}
}
总结
如果异常不应该发生,因为应用程序不支持中断,并且在第一次调用时不应调用Thread.interrupt()
或ThreadGroup.interrupt()
,则将异常转换为AssertionError
是正确的方式。 有两种可能性实现这个目的。
public static void joinThread(final Thread thread) {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.ERROR, "Unexpected InterruptedException", e);
assert false : e;
}
}
public static void joinThread(final Thread thread) {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.FATAL, "Unexpected InterruptedException", e);
throw new AssertionError(e, "Unexpected Thread interruption.");
}
}
如果需要传播InterruptedException
但调用者不支持该异常并且无法更改,您可以将其包装成受支持的异常。是否有效并且是否是一个好主意,这取决于调用者。
public static void joinThread(final Thread thread) {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.ERROR, "Unexpected InterruptedException", e);
throw new SomeException(e, "Unexpected Thread interruption.");
}
}
最后的注意事项
有些人认为始终不应该使用“吞掉(swallowing)”操作,这是不正确的。如果你有100行代码并且在某个地方使用Thread.sleep()
操作有一些好的理由,那么吞掉异常通常是可以接受的。像往常一样,这取决于具体情况。最终,Thread.interrupt()
的语义与SIGHUP
并没有太大差别,而且SIGHUP
通常被忽略。
也有一些人讨论将其转换为AssertionError
是否明智。 AssertionError
是一个未经检查的Throwable
,甚至不是一个Exception
,这意味着编译器不强制代码处理它,并且大多数代码也没有写来处理它 - 这是有意的,因为通过使用AssertionError
,我们表明出现了一种错误,这种错误是如此意外以至于代码不知道如何处理它,因此希望停止运行。无论AssertionError
是否终止VM取决于哪些线程会由于AssertionError
被抛到受影响的Thread
的UncaughtExceptionHandler
中而被关闭。