如何触发Thread.sleep抛出的InterruptedException?

简介: 其实说来也简单,就是Thread.sleep所在的线程被interrupt了就抛出InterruptedException,但由于历史遗留问题这个十分罕见的情况变成了所有开发者都需要处理的常情,从这个细节的设计可以看出Thread这个类的历史遗留相当严重,其实java早已无法完全兼容旧版本,删除废弃了很多API,但仍然无法改良这些关键部分的设计。

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的阿里云开发者社区。

目录
相关文章
|
缓存 NoSQL Java
面试官:如何保证本地缓存的一致性?
面试官:如何保证本地缓存的一致性?
2545 1
|
缓存 Java 应用服务中间件
Tomcat是如何打破"双亲委派"机制的?
上文我们详细了解了类加载以及什么是双亲委派机制,相信很多童鞋都了解Tomcat打破了双亲委派机制,本文将对Tomcat为什么要打破双亲委派机制,以及Tomcat是如何打破双亲委派机制的,进行完整性的复盘与解析。
3796 0
Tomcat是如何打破"双亲委派"机制的?
|
消息中间件 SQL 存储
超详细的RabbitMQ入门,看这篇就够了!
RabbitMQ入门,看这篇就够了
218881 69
|
存储 JSON fastjson
再也不用心惊胆战地使用FastJSON了——序列化篇
本篇将主要介绍json序列化的详细流程。本文阅读的FastJSON源码版本为2.0.31。
3885 49
|
SQL XML Java
MyBatis-Plus多表关联查询
MyBatis-Plus多表关联查询
1504 0
|
消息中间件 Java 中间件
RocketMQ延迟消息的代码实战及原理分析
在RocketMQ中,支持延迟消息,但是不支持任意时间精度的延迟消息,只支持特定级别的延迟消息。如果要支持任意时间精度,不能避免在Broker层面做消息排序,再涉及到持久化的考量,那么消息排序就不可避免产生巨大的性能开销。
3786 0
|
机器学习/深度学习 并行计算 PyTorch
从零开始下载torch+cu(无痛版)
这篇文章提供了一个详细的无痛版教程,指导如何从零开始下载并配置支持CUDA的PyTorch GPU版本,包括查看Cuda版本、在官网检索下载包名、下载指定的torch、torchvision、torchaudio库,并在深度学习环境中安装和测试是否成功。
从零开始下载torch+cu(无痛版)
|
安全 Java Maven
MapStruct使用教程2024(高级版)
MapStruct使用教程2024(高级版)
|
SQL XML Java
【MyBatis】 MyBatis与MyBatis-Plus的区别
【MyBatis】 MyBatis与MyBatis-Plus的区别
7185 0
【MyBatis】 MyBatis与MyBatis-Plus的区别
|
XML Java 数据库连接
无痛事务管理:Spring中的@Transactional和相关注解完全解析
无痛事务管理:Spring中的@Transactional和相关注解完全解析
1856 0