终止线程
中断状态是线程的一个标识位,中断操作是一种简便的线程间交互方式,而这种方式适合用来取消或停止任务。
线程的终止有两种情况:
在线程A里优雅地终止线程B,所谓优雅就是能让线程B做一些线程停止的工作,而不是强制杀死
优雅终止有一套成熟的方案,叫做两阶段终止模式,如下:
在 Java 中,线程的状态有 Runnable, Waiting, Timed_Waiting, 当要终止一个程序时,线程有可能处在等待状态,需要有一种能力将线程从等待状态转换到Runnable状态,让线程能继续运行,Java 中提供了 interrupt(); 等到线程恢复运行后,一种方式是等待run()运行完毕,一般是提供一个线程标识位,用来标识该线程已经被中断,检测到后就可以处理中断程序。所以,Java 中的两阶段终止模式是:interrupt() 方法和线程终止状态位
一个实际例子
监控系统需要动态地采集一些数据,一般都是监控系统发送采集指令给被监控系统的监控代理,监控代理接收到指令之后,从监控目标收集数据,然后回传给监控系统;动态采集功能一般都会有终止操作。
根据两阶段终止模式的实现:
public class MonitorSystemProxy {
// 保证只启动一次
private boolean started = false;
private Thread rptThread;
// 开启采集功能
public synchronized void start() {
if (started) {
return;
}
started = true;
rptThread = new Thread(() -> {
// 线程终止状态位,检测到 interrupt 指令就退出
while (!Thread.currentThread().isInterrupted()) {
// 采集上报的逻辑实现
// report();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// 当 catch 到 InterruptedException 状态标识位被清空
// 重新设置状态位
Thread.currentThread().interrupt();
}
}
started = false;
});
rptThread.start();
}
// 终止采集功能
public synchronized void stop() {
// 发送终止命令,interrupt()
rptThread.interrupt();
}
}
以上实现已经能基本工作了,但是有一个隐患,在于采集上报的逻辑里可能会有代码捕获 InterruptedException 异常,导致状态位被清空,Thread.currentThread().isInterrupted() 判断失效,因为我们可能用了第三方库,无法修改,所以为代码埋下了隐患。
由于线程自身的状态标识位有可能出问题,只能我们自己独立维护一个是否中断的状态位。代码如下:
public class MonitorSystemProxyOpt {
// 自定义的中断标识位
private volatile boolean terminated = false;
// 启动标识
private boolean started = false;
// 采集上报线程
private Thread rptThread;
// 开启采集功能
public synchronized void start() {
if (started) {
return;
}
started = true;
terminated = false;
rptThread = new Thread(() -> {
while (!terminated) {
// 采集上报的逻辑实现
// report();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// 重新设置状态位
Thread.currentThread().interrupt();
}
}
started = false;
});
rptThread.start();
}
// 终止采集功能
public synchronized void stop() {
// 设置中断
terminated = true;
// 中断线程
rptThread.interrupt();
}
}
线程池的终止和退出
线程池提供了 shutdown() 和 shutdownNow().