多线程之两阶段终止模式
两阶段终止模式
当某些线程正在运行时,如何去正确终止线程,如果直接强制终止线程(如调用 Thread.stop()
)会带来以下问题:
- 资源未释放:线程可能持有锁、打开文件或网络连接未关闭。
- 数据不一致:线程可能正在修改共享数据,突然终止会导致数据损坏。
- 不可控性:无法保证线程在安全点终止。
两阶段终止模式(Two-Phase Termination Pattern)是一种多线程编程中安全终止线程的设计模式。它的核心思想是:在终止线程前,先发出终止请求(第一阶段),然后线程在完成必要清理工作后再真正终止(第二阶段)。这种模式避免了直接强制终止线程导致的资源泄露、数据不一致等问题
简单来说就是在一个线程T1中如何优雅的终止线程T2,给T2一个料理后事的机会
两阶段终止的实现步骤
第一阶段:发出终止请求
- 设置一个终止标志位,通知线程需要终止。
- 如果线程处于阻塞状态(如
sleep()
、wait()
),需要通过**中断(Interrupt)**唤醒它。
第二阶段:线程响应终止请求
- 线程检测到终止标志位或中断信号后,停止接受新任务。
- 执行清理工作(如释放锁、关闭文件)。
- 安全终止线程。
如果你依然认为难以理解,可以用狼人杀的角度来解释一下:
使用
Thread.stop()
等强制终止线程,就是夜间被狼刀了,而两阶段终止模式是白天被票决,你还有发表遗言的机会
代码实现
这里启用了一个监控线程,在监控线程中用了一个死循环,并且用线程的中断状态作为标志位,如果线程在运行时被打断可直接进入第二阶段,若是线程在睡眠中被打断,在捕获InterruptedException后,重新设置中断状态,以便标志位终止线程
public class TwoPhase {
/**
* 两阶段终止模式:
* 在一个线程T1中如何优雅的终止线程T2,给T2一个料理后事的机会
*/
private Thread monitor;
// 启动监控线程
public void start(){
monitor = new Thread(() -> {
while (true){
Thread current = Thread.currentThread();
if (current.isInterrupted()){ // 判断线程中断状态
System.err.println("线程被打断,料理后事中.............");
break;
}
try {
Thread.sleep(2000);
System.out.println("记录线程日志--------------------");
} catch (InterruptedException e) {
e.printStackTrace();
System.err.println("------------->重置线程打断标识");
current.interrupt();
}
}
System.out.println("--------------线程已终止-----------------");
});
monitor.start();
}
// 终止监控线程
public void stop(){
monitor.interrupt();
}
}
此时可以创建一个测试类,启动监控线程,等待几秒之后用stop终止该线程
public class TwoPhaseTest {
public static void main(String[] args) throws InterruptedException {
TwoPhase twoPhase = new TwoPhase();
twoPhase.start();
Thread.sleep(7000);
twoPhase.stop();
}
}
控制台会依次输出信息:
记录线程日志--------------------
记录线程日志--------------------
记录线程日志--------------------
java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at juc2.TwoPhase.lambda$start$0(TwoPhase.java:20)
at java.base/java.lang.Thread.run(Thread.java:834)
------------->重置线程打断标识
线程被打断,料理后事中.............
--------------线程已终止-----------------