优雅的结束线程是指线程T1中去结束线程T2,并且让T2有机会处理结束线程的流程,那么该如何实现呢?
1.两阶段终止模式
两阶段的含义:
- 第一阶段:向线程发送终止指令
- 第二阶段:响应终止指令
1.1.第一阶段
Java线程进入终止状态的前提是该现在处于Runnable状态,但是此时线程可能处于休眠状态。而Java提供的interrupt方法它可以将休眠状态的线程转换到 RUNNABLE 状态。
1.2.第二阶段
而Runnable接收到指令后,该如何进入终止状态呢?
- 异常
- 执行完run方法,此时能够设置终止标示位,根据标示位去结束线程的执行
示例代码如下:
设置了自己的终止线程标志位
class Proxy {
// 线程终止标志位
volatile boolean terminated = false;
boolean started = false;
// 采集线程
Thread rptThread;
// 启动采集功能
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();
}
// 终止采集功能
synchronized void stop(){
// 设置中断标志位
terminated = true;
// 中断线程 rptThread
rptThread.interrupt();
}
}
为什么需要两阶段终止,我直接检查标志位或者线程的中断状态不可以吗?
两阶段终止模式是一种应用很广泛的并发设计模式,在 Java 语言中使用两阶段终止模式来优雅地终止线程,需要注意两个关键点:一个是仅检查终止标志位是不够的,因为线程的状态可能处于休眠态;另一个是仅检查线程的中断状态也是不够的,因为我们依赖的第三方类库很可能没有正确处理中断异常。
2.如何优雅的终止线程池
线程池提供了两个方法:**shutdown()*和*shutdownNow()
2.1.shutdown()
线程池执行shutdown后,就会拒绝接受新的任务,但是会将线程池中的任务全部执行完
2.2.shutdownNow()
该方法会拒绝接受新的任务,同时还会中断线程池中正在执行的任务,已经进入阻塞队列的任务也被剥夺了执行的机会,不过队列中的这些任务会作为返回值返回。因为该方法中断了线程池中正在执行的任务,所以这些任务需要优雅的结束,接需要正确的处理线程中断