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

目录
相关文章
|
Java Spring
【注解】Spring AOP 面向切面编程之@Around的详细用法
【注解】Spring AOP 面向切面编程之@Around的详细用法
2939 0
|
缓存 NoSQL Java
面试官:如何保证本地缓存的一致性?
面试官:如何保证本地缓存的一致性?
2278 1
|
缓存 Java 应用服务中间件
Tomcat是如何打破"双亲委派"机制的?
上文我们详细了解了类加载以及什么是双亲委派机制,相信很多童鞋都了解Tomcat打破了双亲委派机制,本文将对Tomcat为什么要打破双亲委派机制,以及Tomcat是如何打破双亲委派机制的,进行完整性的复盘与解析。
3616 0
Tomcat是如何打破"双亲委派"机制的?
|
消息中间件 SQL 存储
超详细的RabbitMQ入门,看这篇就够了!
RabbitMQ入门,看这篇就够了
216112 67
|
缓存 Java 程序员
Spring中异步注解@Async的使用、原理及使用时可能导致的问题
本文主要介绍了Spring中异步注解的使用、原理及可能碰到的问题,针对每个问题文中也给出了方案。希望通过这篇文章能帮助你彻底掌握`@Async`注解的使用,知其然并知其所以然!
14178 4
|
10月前
|
存储 JSON fastjson
再也不用心惊胆战地使用FastJSON了——序列化篇
本篇将主要介绍json序列化的详细流程。本文阅读的FastJSON源码版本为2.0.31。
3188 49
|
安全 Java 编译器
是时候来唠一唠synchronized关键字了,Java多线程的必问考点!
本文简要介绍了Java中的`synchronized`关键字,它是用于保证多线程环境下的同步,解决原子性、可见性和顺序性问题。从JDK1.6开始,synchronized进行了优化,性能得到提升,现在仍可在项目中使用。synchronized有三种用法:修饰实例方法、静态方法和代码块。文章还讨论了synchronized修饰代码块的锁对象、静态与非静态方法调用的互斥性,以及构造方法不能被同步修饰。此外,通过反汇编展示了`synchronized`在方法和代码块上的底层实现,涉及ObjectMonitor和monitorenter/monitorexit指令。
1224 0
|
消息中间件 Java 中间件
RocketMQ延迟消息的代码实战及原理分析
在RocketMQ中,支持延迟消息,但是不支持任意时间精度的延迟消息,只支持特定级别的延迟消息。如果要支持任意时间精度,不能避免在Broker层面做消息排序,再涉及到持久化的考量,那么消息排序就不可避免产生巨大的性能开销。
3587 0
|
消息中间件 JSON Java
Spring Boot、Spring Cloud与Spring Cloud Alibaba版本对应关系
Spring Boot、Spring Cloud与Spring Cloud Alibaba版本对应关系
22529 0
|
存储 Java 应用服务中间件
Java规则引擎Drools急速入门
Java规则引擎Drools急速入门
Java规则引擎Drools急速入门