前言
想必大家都知道interrupt()函数,从这个单词上【interrupt 英[ˌɪntəˈrʌpt],美[ˌɪntəˈrʌpt],desc:打断; 打扰; 插嘴; 打岔; 使暂停; 使中断; 阻断,遮挡】,看起来是中断的含义,那么它真的能让运行到一半的线程中止掉吗?
Interrupt函数
据上图根据官方解释,在调用interrupt函数以后,收到InterruptedException异常的前提是该线程“阻塞的调用wait , sleep ,或join的方法”,那么假设某线程不在其列出的阻塞调用中,是否能使用此方法中止并抛出InterruptedException呢?实践出真知,直接操作一把,看能否中断便一目了然。
/**
* @author Duansg
* @date 2022-08-28 10:19 下午
*/
@SuppressWarnings("all")
public class InterruptMain {
public static void main(String[] args) {
final Thread thread = new Thread(() -> {
for (; ; ) {
System.out.println("exit");
}
});
thread.start();
thread.interrupt();
System.out.println("main stop");
}
}
根据伪代码执行并不能中止,也不会抛出InterruptedException,如按官方文档所描述,增加调用阻塞方法即可得到InterruptedException,【需要注意的是在阻塞的情况中断,不仅会得到中断异常,还会恢复中断标志位】但是子线程还是会继续输出“exit”,那么如何中断?
import java.util.concurrent.TimeUnit;
/**
* @author Duansg
* @date 2022-08-28 10:19 下午
*/
@SuppressWarnings("all")
public class InterruptMain {
public static void main(String[] args) {
final Thread thread = new Thread(() -> {
for (; ; ) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("InterruptedException exit");
break;
}
}
});
thread.start();
thread.interrupt();
System.out.println("main stop");
}
}
由此上代码,捕获了InterruptedException,然后手动停止了当前线程,到此不难理解interrupt函数向某个线程发送中断请求,这个请求操作是将被请求线程的中断标志位设置为true,重要的是它仅仅只是发送中断请求,至于这个线程能否被中断,这取决于被请求线程是否检查了中断标志位[可以使用isInterrupted()]或者对中断异常做出对应的响应。
为什么是轻量级阻塞?
java线程的状态
// java.lang.Thread.State
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
java线程状态的流转过程
此上我们不难看出如果调用了阻塞函数,线程都会进入WAITTING/TIMED_WAITTING状态,也只有在这个状态中的线程,我们才可以进行响应中断,所以如果你看到这里,你可以将其interrupt函数理解为“唤醒一个轻量阻塞的线程”。
为什么不是重量级阻塞?
为什么处于BLOCKED状态的线程不能响应中断?大家可以去看看Thread提供的阻塞函数中,InterruptedException作为一个检查异常而存在,也就是相当于我们在方法中 throw new InterruptedException();而synchronized作为一个关键字,是无法抛出检查异常的。而一个线程排除死锁问题,其不会一致处在BLOCKED状态中的,只有等BLOCKED恢复到运行状态,通过响应中断进行处理。
如何检查中断?
除了可以捕获InterruptedException进行响应中断,我们还可以根据提供的两个方法进行检查中断标志位,如下。
为什么interrupted()需要清除中断标志
在大部分场景下,我们一旦检测到中断,会进行响应处理,也就是此次的中断我已经响应并处理了,如果不清除,那么假设线程还在执行,程序上不仅要处理循环检查等问题,还无法判断中断次数,清除本次中断标志,有助于感知下次中断。
是响应中断异常还是抛出异常?
这个问题取决于你的逻辑处理,甚至你可以输出摘要日志进行人工补偿,但其处理方式,笔者认为跟其他处理异常方式无差别,当你明确知道如何处理这个异常的时候就可以捕获,如果当前处理不了就抛出或者忽略。
在谈中断的意义
中断其目的是为了优雅的停止某个线程,能让线程感知自身已经被列为中断线程,然后进行一些必要逻辑的处理。假设没有响应中断,直接调用 Thread.stop() 方法【已过时】,线程直接被杀掉,那么这个线程可能拥有的资源因为非正常的关闭导致一系列的问题发生。
本文回顾
- interrupt()方法:中断("唤醒")轻量级阻塞线程,仅仅发送请求给线程设置一个中断标志,但是线程依旧会执行。
- interrupted()方法:Thread类的静态方法。检查当前线程的中断标志,返回一个boolean并清除中断状态,其连续两次调用的返回结果不一样,因为第二次调用的时候线程的中断状态已经被清除,会返回一个false。
- isInterrupted()方法:测试线程是否被中断,不会清除中断状态。