前言
想必大家都知道 interrupt()函数,从这个单词上【interrupt 英[ˌɪntəˈrʌpt],美[ˌɪntəˈrʌpt],desc:打断; 打扰; 插嘴; 打岔; 使暂停; 使中断; 阻断,遮挡】,看起来是中断的含义,那么它真的能让运行到一半的线程中止掉吗?
Interrupt 函数
据上图根据官方解释,在调用 interrupt 函数以后,收到 InterruptedException 异常的前提是该线程“阻塞的调用 wait , sleep ,或 join 的方法”,那么假设某线程不在其列出的阻塞调用中,是否能使用此方法中止并抛出 InterruptedException 呢?实践出真知,直接操作一把,看能否中断便一目了然。
1 | /** |
根据伪代码执行并不能中止,也不会抛出 InterruptedException,如按官方文档所描述,增加调用阻塞方法即可得到 InterruptedException,【需要注意的是在阻塞的情况中断,不仅会得到中断异常,还会恢复中断标志位】但是子线程还是会继续输出“exit”,那么如何中断?
1 | import java.util.concurrent.TimeUnit; |
由此上代码,捕获了 InterruptedException,然后手动停止了当前线程,到此不难理解 interrupt 函数向某个线程发送中断请求,这个请求操作是将被请求线程的中断标志位设置为 true,重要的是它仅仅只是发送中断请求,至于这个线程能否被中断,这取决于被请求线程是否检查了中断标志位[可以使用 isInterrupted()]或者对中断异常做出对应的响应。
为什么是轻量级阻塞?
java 线程的状态
1 | // java.lang.Thread.State |
java 线程状态的流转过程
此上我们不难看出如果调用了阻塞函数,线程都会进入 WAITTING/TIMED_WAITTING 状态,也只有在这个状态中的线程,我们才可以进行响应中断,所以如果你看到这里,你可以将其 interrupt 函数理解为“唤醒一个轻量阻塞的线程”。
为什么不是重量级阻塞?
为什么处于 BLOCKED 状态的线程不能响应中断?大家可以去看看 Thread 提供的阻塞函数中,InterruptedException 作为一个检查异常而存在,也就是相当于我们在方法中 throw new InterruptedException();而 synchronized 作为一个关键字,是无法抛出检查异常的。而一个线程排查死锁问题,其不会一致处在 BLOCKED 状态中的,只有等 BLOCKED 恢复到运行状态,通过响应中断进行处理。
如何检查中断?
除了可以捕获 InterruptedException 进行响应中断,我们还可以根据提供的两个方法进行检查中断标志位,如下。
为什么 interrupted()需要清除中断标志
在大部分场景下,我们一旦检测到中断,会进行响应处理,也就是此次的中断我已经响应并处理了,如果不清除,那么假设线程还在执行,程序上不仅要处理循环检查等问题,还无法判断中断次数,清除本次中断标志,有助于感知下次中断。
是响应中断异常还是抛出异常?
这个问题取决于你的逻辑处理,甚至你可以输出摘要日志进行人工补偿,但其处理方式,笔者认为跟其他处理异常方式无差别,当你明确知道如何处理这个异常的时候就可以捕获,如果当前处理不了就抛出或者忽略。
在谈中断的意义
中断其目的是为了优雅的停止某个线程,能让线程感知自身已经被列为中断线程,然后进行一些必要逻辑的处理。假设没有响应中断,直接调用 Thread.stop() 方法【已过时】,线程直接被杀掉,那么这个线程可能拥有的资源因为非正常的关闭导致一系列的问题发生。
本文回顾
- interrupt()方法:中断(“唤醒”)轻量级阻塞线程,仅仅发送请求给线程设置一个中断标志,但是线程依旧会执行。
- interrupted()方法:Thread 类的静态方法。检查当前线程的中断标志,返回一个 boolean 并清除中断状态,其连续两次调用的返回结果不一样,因为第二次调用的时候线程的中断状态已经被清除,会返回一个 false。
- isInterrupted()方法:测试线程是否被中断,不会清除中断状态。