当需要对正在执行指令的线程(或线程池任务)执行终止操作时,可以直接调用线程或线程池提供的Api,
1、线程
Thread提供了stop方法,强制停止正在执行的线程任务,除非自己的任务正执行一半可以被停止【比如采集日志本身就允许采样率等】。否则一般可以使用interrupt方法执行终端操作。
2、线程池
线程池(如ThreadPoolExecutor 父类ExecutorService)提供了shutdown方法,在执行完任务后自动关闭线程。当然我们也可以暴力的使用shutdownNow方法,使用场景如上,或者在下一次线程池启动时能将上次的任务执行完成。
但是需要注意,线程的生命周期是 start启动后,就进入runnable允许CPU时间切片切中运行,也运行进入Blocking状态,那么我们需要将线程进行终止则需要将Blocking状态的线程切换为Runnable状态才能进行关闭。
实现终止任务的模式,一般的做法是设置一个标志位,线程会在执行任务的时候找个合适的位置检查标志位,如果标志位发发生了变化,则终止我们正在执行的任务,后面的阶段称为响应终止指令,这就是两阶段终止模式。
我们可以直接使用线程或线程池原生的Api即可达到终止正在执行线程池的任务,但是线上项目的环境是非常复杂的,我们没有办法保证第三方类库在获取到终端异常后,会重新设置标志位,(即第三方类库将Interrupt异常吃掉了),所以一般都是使用自己的终端标志位,如
/** 设置自己的中断标志位 */
private volatile boolean terminated = false;
所以最终写法大致类似:
public class TerminatedTwoStageTermination {
/** 设置自己的中断标志位 */
private volatile boolean terminated = false;
boolean started = false;
/** 采集线程 */
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;
// 中断线程,即使是 TIMED_WAITING状态的线程,然后进行下一次判断 自己的终止标志位 terminated
rptThread.interrupt();
}
/** 模拟执行任务 */
private void report() {
}
}