【高并发】由InterruptedException异常引发的思考
写在前面
InterruptedException异常可能没你想的那么简单!
前言
当我们在调用Java对象的wait()方法或者线程的sleep()方法时,需要捕获并处理InterruptedException异常。如果我们对InterruptedException异常处理不当,则会发生我们意想不到的后果!
程序案例
例如,下面的程序代码,InterruptedTask类实现了Runnable接口,在run()方法中,获取当前线程的句柄,并在while(true)循环中,通过isInterrupted()方法来检测当前线程是否被中断,如果当前线程被中断就退出while(true)循环,同时,在while(true)循环中,还有一行Thread.sleep(100)代码,并捕获了InterruptedException异常。整个代码如下所示。
package io.binghe.concurrent.lab08;
/**
* @author binghe
* @version 1.0.0
* @description 线程测试中断
*/
public class InterruptedTask implements Runnable{
@Override
public void run() {
Thread currentThread = Thread.currentThread();
while (true){
if(currentThread.isInterrupted()){
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
上述代码的本意是通过isInterrupted()方法检查线程是否被中断了,如果中断了就退出while循环。其他线程通过调用执行线程的interrupt()方法来中断执行线程,此时会设置执行线程的中断标志位,从而使currentThread.isInterrupted()返回true,这样就能够退出while循环。
这看上去没啥问题啊!但真的是这样吗?我们创建一个InterruptedTest类用于测试,代码如下所示。
package io.binghe.concurrent.lab08;
/**
* @author binghe
* @version 1.0.0
* @description 测试线程中断
*/
public class InterruptedTest {
public static void main(String[] args){
InterruptedTask interruptedTask = new InterruptedTask();
Thread interruptedThread = new Thread(interruptedTask);
interruptedThread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
interruptedThread.interrupt();
}
}
我们运行main方法,如下所示。
这竟然跟我们想象的不一样!不一样!不一样!这是为什么呢?
问题分析
上述代码明明调用了线程的interrupt()方法来中断线程,但是却并没有起到啥作用。原因是线程的run()方法在执行的时候,大部分时间都是阻塞在sleep(100)上,当其他线程通过调用执行线程的interrupt()方法来中断执行线程时,大概率的会触发InterruptedException异常,在触发InterruptedException异常的同时,JVM会同时把线程的中断标志位清除,所以,这个时候在run()方法中判断的currentThread.isInterrupted()会返回false,也就不会退出当前while循环了。
既然问题分析清除了,那如何中断线程并退出程序呢?
问题解决
正确的处理方式应该是在InterruptedTask类中的run()方法中的while(true)循环中捕获异常之后重新设置中断标志位,所以,正确的InterruptedTask类的代码如下所示。
package io.binghe.concurrent.lab08;
/**
* @author binghe
* @version 1.0.0
* @description 中断线程测试
*/
public class InterruptedTask implements Runnable{
@Override
public void run() {
Thread currentThread = Thread.currentThread();
while (true){
if(currentThread.isInterrupted()){
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
currentThread.interrupt();
}
}
}
}
可以看到,我们在捕获InterruptedException异常的catch代码块中新增了一行代码。
currentThread.interrupt();
这就使得我们捕获到InterruptedException异常后,能够重新设置线程的中断标志位,从而中断当前执行的线程。
我们再次运行InterruptedTest类的main方法,如下所示。
总结
处理InterruptedException异常时要小心,如果在调用执行线程的interrupt()方法中断执行线程时,抛出了InterruptedException异常,则在触发InterruptedException异常的同时,JVM会同时把执行线程的中断标志位清除,此时调用执行线程的isInterrupted()方法时,会返回false。此时,正确的处理方式是在执行线程的run()方法中捕获到InterruptedException异常,并重新设置中断标志位(也就是在捕获InterruptedException异常的catch代码块中,重新调用当前线程的interrupt()方法)。
写在最后
如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发编程技术。
最后,附上并发编程需要掌握的核心技能知识图,祝大家在学习并发编程时,少走弯路。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端