InterruptedException由Sun Microsystems公司的前任工程师Frank Yellin编写,发布于JDK1.0沿用至今,不过它并不是一个常见的Exception,只有在直接或间接调用如下方法的时候才会触发它
java.lang.Object | java.lang.Thread |
---|---|
wait() | join() |
wait(long) | join(long) |
wait(long, int) | join(long, int) |
sleep(long) | |
sleep(long, int) |
以上方法可都会throws出InterruptedException,但InterruptedException并不常见,例如Thread.sleep(long)
就经常被用来休眠线程,却很少会遇到InterruptedException,那么究竟是在什么情况下才会触发它呢?请看这个例子:
// demo1
Thread.currentThread().interrupt();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread中interrupt()
方法是个关键,它使线程陷入中断状态,在中断状态中调用sleep方法,立刻触发如下异常
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
...
这是正确的结果,因为上面的例子调用interrupt()
中断了线程,然后又休眠线程,这是不合理的。
但是这又是有用的,正如sleep()的Javadoc中所说
@throws InterruptedException
if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.
意思是说,如果当前线程被中断,则会抛出此异常,并且清除当前线程的中断状态。
那么中断状态被清除掉之后,是否就可以使用sleep()呢?下面尝试一下在demo1的catch块中再次sleep
// demo2
Thread.currentThread().interrupt();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
try {
Thread.sleep(200);
System.out.println("success");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
此时demo2成功运行,没有进入第二个catch块,输出
success
果如文档所说,但我觉得这真的是一个神奇又有点搞笑的操作。
在单线程的情况下并不容易遇到,因为在线程interrupt后休眠线程不合情理,也很少有情况会interrupt当前的线程,但在多线程的情况下,确实不易感觉到
Thread foo = new Thread(() -> {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
foo.start();
foo.interrupt();
然后就能看到这个报错了:
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
...
线程foo启动后调用了interrupt()
,此时foo尚未正式运行,因为启动线程需要一定的时间,而foo线程被interrupt后还在运行,执行sleep时就会遇到这个异常。问题的关键就在于只要休眠一个处于中断状态的线程,InterruptedException自然会随之而来。
总结
如果一个线程被interrupt
,并在之后进行sleep()
,wait()
,join()
这三种操作,那么InterruptedException就会被触发。如果你确保你的线程在自己的控制之中,不会莫名其妙的去interrupt()
,就可以当这个异常不存在,这个是几乎不太可能遇到的异常。
说实话感觉这个异常真的意义有限,既不会因为外部因素导致异常,也没有什么利用价值,如果放到现在重新设计,最起码也是把这个异常设成RuntimeException
吧,强制抛这种几乎没人在意的错,就没想过所有人在调用Thread.sleep()
时都要多写这一大坨try-catch
块吗?
如本文开头所说,这是JDK1.0留下来的历史遗留问题,这么不合理的异常设计遗留到现在,可以说Thread
这整个类已经写了两千多行了,已经是一大坨了,很难改了。照理来说应该和java.util.Date
一样直接开个新的类,可以保留原有的类,但后面关于线程的东西都重新设计,并且覆盖之前的类的功能,但JDK19新出的虚拟线程Thread.startVirtualThread()
还要丢进这个破类,真是不思进取,下头。
本文写作于2018年11月14日发布于lyrieek的简书,于2023年7月13日进行修订发布于lyrieek的阿里云开发者社区。