Two-Phase Termination模式
⭐分俩段终止
🐶简介
⛺正常处理的线程状态为操作中
,在停止该线程时,线程会先开始打扫工作,从操作中
变为 终止处理中
。在终止处理中
线程不会突然终止,而是会
st=>start: 开始
e=>end: 终止
op1=>operation: 操作中
op2=>operation: 终止处理中
st->op1->op2->e
🐯该模式的主要特点如下:
. 安全的终止线程(安全性)
. 必定会终止线程(生存性)
. 发出终止指令后尽快进行终止处理(响应性)
🐉实例程序
main类
public class Main {
public static void main(String[] args) {
System.out.println("main: BEGIN");
try {
// 启动线程
CountupThread t = new CountupThread();
t.start();
// 稍微间隔一段时间
Thread.sleep(10000);
// 线程的终止请求
System.out.println("main: shutdownRequest");
t.shutdownRequest();
System.out.println("main: join");
// 等待线程终止
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main: END");
}
}
CountThread类
. shudownRequest
方法无需设置为synchronized,终止标记时一旦被设置为true后就不会再变为false的闭锁。
public class CountupThread extends Thread {
// 计数值
private long counter = 0;
// 发出终止请求后变为true
private volatile boolean shutdownRequested = false;
// 终止请求
public void shutdownRequest() {
shutdownRequested = true;
// 确保线程在sleep和wait时也会被终止
interrupt();
}
// 检查是否发出了终止请求
public boolean isShutdownRequested() {
return shutdownRequested;
}
// 线程体
public final void run() {
try {
while (!isShutdownRequested()) {
doWork();
}
} catch (InterruptedException e) {
} finally {
doShutdown();
}
}
// 操作
private void doWork() throws InterruptedException {
counter++;
System.out.println("doWork: counter = " + counter);
Thread.sleep(500);
}
// 终止处理
private void doShutdown() {
System.out.println("doShutdown: counter = " + counter);
}
}
🐱登场角色
TerminationRequest(终止请求发出者)
向Terminator
发送终止请求
Terminator(终止者)
- 接受终止请求,并执行终止处理,
shutdownRequest
方法不需要synchronized。 Terminator
带有一个表示自己是否已经接受终止请求的标志(闩)。- 可能会编写
teminator
的子类,doWork
和doShutDown
设为protext方法。
🐰拓展思路
- 不能使用
Thread.stop
方法,处于访问临界区的代码也会终止,无法保证实例安全性 - 仅仅检查标志是不够的,线程在sleep、wait时必须调用
interrupt
方法对线程下达中断指示。 - 仅仅检查中断状态也是不够的,即捕获
InterruptException
,使用isInterrupted
方法来检查线程是否处于终止状态。但是只要线程有一处忽略InterruptException
,上面方法就行不通。 - 在长时间处理前检查终止请求
join
方法和isAlive
方法,join
等待指定线程终止isAlive
确定指定线程是否已经终止。
java.util.concurrent.ExecutorService
接口和Two-Phase Termination
模式isShutdown
方法用于确认shutwown
方法是否已经被调用。isTerminated
方法用于确认线程是否已经实际停止。
- 要捕获程序整体的终止时
- 未捕获异常的处理器
- 使用
Thread.setDefaultUncaughtExceptionHandler
静态方法,可以设置未捕获的异常的处理器,设置了未捕获异常的处理器后,程序将不会输出调用堆栈而是直接退出。
- 使用
- 退出钩子
- 退出钩子是指java虚拟机推出时启动的线程,
java虚拟机退出
是指System.exit()
被调用或是全部非守护线程终止时。 - 可以使用java.lang.Runtime的实例方法
addShutdownHook
方法来编写程序完全终止时的终止处理。 - 示例程序
public class Main { public static void main(String[] args) { System.out.println("main:BEGIN"); // (1) 设置未捕获的异常的处理器 Thread.setDefaultUncaughtExceptionHandler( new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread thread, Throwable exception) { System.out.println("****"); System.out.println("UncaughtExceptionHandler:BEGIN"); System.out.println("currentThread = " + Thread.currentThread()); System.out.println("thread = " + thread); System.out.println("exception = " + exception); System.out.println("UncaughtExceptionHandler:END"); } } ); // (2) 设置退出钩子 Runtime.getRuntime().addShutdownHook( new Thread() { public void run() { System.out.println("****"); System.out.println("shutdown hook:BEGIN"); System.out.println("currentThread = " + Thread.currentThread()); System.out.println("shutdown hook:END"); } } ); // (3) 大约 3 秒后启动执行“整数除零计算”的线程 new Thread("MyThread") { public void run() { System.out.println("MyThread:BEGIN"); System.out.println("MyThread:SLEEP..."); try { Thread.sleep(3000); } catch (InterruptedException e) { } System.out.println("MyThread:DIVIDE"); // 「整数除零计算」 int x = 1 / 0; // 不会来到这里 System.out.println("MyThread:END"); } }.start(); System.out.println("main:END"); } }
- 退出钩子是指java虚拟机推出时启动的线程,
- 未捕获异常的处理器
🐷延申阅读
java.util.concurrent.CountDownLatch类
使用java.util.concurrent.CountDownLatch
类可以实现等待指定次数countDown
方法被调用这一功能。
java.util.concurrent.CyclicBarrier类
CyclicBarrier
可以周期性的创建屏障,在屏障解除之前,碰到屏障的线程无法继续前进,当指定个数的线程到达屏障时,屏障就会被解除。- 在创建
CyclicBarrier
的示例时,可以指定Runnable
对象,这个对象被称为屏障操作,每次屏障解除后,都会执行该操作。
📖参考书籍
- 图解Java多线程设计模式
☀️诗与远方
你无法察觉你脸上淌下的泪水,而我也无法替你逝去。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统