写点什么

【高并发】明明中断了线程,却为何不起作用呢?

用户头像
冰河
关注
发布于: 4 小时前
【高并发】明明中断了线程,却为何不起作用呢?

大家好,我是冰河~~


当我们在调用 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()方法)。


好了,今天就到这儿吧,我是冰河,我们下期见~~

发布于: 4 小时前阅读数: 7
用户头像

冰河

关注

公众号:冰河技术 2020.05.29 加入

互联网高级技术专家,《深入理解分布式事务:原理与实战》,《海量数据处理与大数据技术实战》和《MySQL技术大全:开发、优化与运维实战》作者,mykit-data与mykit-transaction-message框架作者。【冰河技术】作者。

评论 (1 条评论)

发布
用户头像
原创不易,冰河在线求三连,不过分吧?
4 小时前
回复
没有更多了
【高并发】明明中断了线程,却为何不起作用呢?