定义:通过将停止线程这个动作分解为准备阶段和执行阶段两个阶段,提供了一种通用的用于优雅地停止线程的方法
准备阶段:“通知”目标线程(欲停止的线程)准备进行停止,会设置一个标志变量用于指示目标线程可以准备停止了
停止阶段:检查准备阶段所设置的线程停止标志和信号,在此基础上决定线程停止的时机,并进行适当的“清理”操作
ThreadOwner: 目标线程的拥有者。一般讲目标线程的创建者视为该线程的拥有者,并假定其“知道”目标线程的工作内容,可以安全地停止目标线程
Terminatable: 可停止线程的抽象
terminate: 请求目标线程终止
AbstractTerminatableThread: 可停止的线程
terminate: 设置线程停止标志,并发送停止“信号”给目标线程
doTerminate: 留给子类实现线程停止时所需的一些额外操作,如目标线程代码中包含Socket I/O,子类可以在该方法中关闭socket以达到快速停止线程,而不会使目标线程等待I/O完成才能侦测到线程停止标记
doRun: 线程处理逻辑方法。留给子类实现线程的处理逻辑,相当于Thread.run(),只不过该方法中无须关心停止线程的逻辑,因为这个逻辑已经被封装在TerminatableThread的run方法中
doCleanup: 留给子类实现线程停止后可能需要的一些清理动作
TerminationToken: 线程停止标志。toShutdown用于指示目标线程可以停止了。reservations可用于反映目标线程还有多少数量未完成的任务,以支持等目标线程处理完任务后再进行停止
ConcreteTerminatableThread: 由应用自己实现的AbstractTerminatableThread参与者的实现类。该类需要实现其父类的doRun()抽象方法,在其中实现线程的处理逻辑,并根据应用的实际需要覆盖其父类的doTerminate方法、doCleanup方法
下列代码中告警发送线程停止需要解决两个问题,一个是将缓存队列中的消息发送出去,一个是用于缓存消息的阻塞队列为空时发送线程会处于等待状态,无法响应关闭线程的请求,因此此处使用两阶段终止模式来解决这个问题。设置一个计数器reservations,消息缓存到队列是加1,消息发送成功是减1,如果值为0,则可以确定线程无未处理任务,可以关闭。执行终止方法AbstractTerminatableThread#terminate()时先将标记toShutdown设置为true,此为准备阶段,然后执行终止逻辑,如果缓存队列中无待处理的任务,则强制终止线程(即调用super.interrupt()方法)。执行阶段AbstractTerminatableThread#run()会判断toShutdown和resvations的值进行终止线程,然后还会执行一些清理。
package com.bruce.twoPhaseTermination;
/**
* @Author: Bruce
* @Date: 2019/5/31 12:31
* @Version 1.0
*/
public interface Terminatable {
void terminate();
}
package com.bruce.twoPhaseTermination;
import java.lang.ref.WeakReference;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author: Bruce
* @Date: 2019/5/31 12:55
* @Version 1.0
*/
public class TerminationToken {
protected volatile boolean toShutdown = false;
public final AtomicInteger reservations = new AtomicInteger(0);
private final Queue<WeakReference<Terminatable>> coordinatedThreads;
public TerminationToken() {
coordinatedThrea