Two Phase Termination
在一个线程 T1 中如何“优雅”终止线程 T2?
这里的【优雅】指的是给 T2 一个料理后事的机会。
这里终止过程分成两个阶段:一阶段主要是线程T1向线程T2发送终止指令,二阶段就是线程T2响应终止指令。
1. 错误思路
- 使用线程对象的 stop() 方法停止线程
- stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其它线程将永远无法获取锁
- stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其它线程将永远无法获取锁
- 使用 System.exit(int) 方法停止线程
- 目的仅是停止一个线程,但这种做法会让整个程序都停止
2. 两阶段终止模式
2.1 利用 isInterrupted
interrupt 可以打断正在执行的线程,无论这个线程是在 sleep,wait,还是正常运行
class TPTInterrupt {private Thread thread;public void start(){thread = new Thread(() -> {while(true) {Thread current = Thread.currentThread();if(current.isInterrupted()) {log.debug("料理后事");break;}try {Thread.sleep(1000);log.debug("将结果保存");} catch (InterruptedException e) {current.interrupt();}// 执行监控操作}},"监控线程");thread.start();}public void stop() {thread.interrupt();}}
调用
TPTInterrupt t = new TPTInterrupt();t.start();Thread.sleep(3500);log.debug("stop");t.stop();
结果
11:49:42.915 c.TwoPhaseTermination [监控线程] - 将结果保存11:49:43.919 c.TwoPhaseTermination [监控线程] - 将结果保存11:49:44.919 c.TwoPhaseTermination [监控线程] - 将结果保存11:49:45.413 c.TestTwoPhaseTermination [main] - stop11:49:45.413 c.TwoPhaseTermination [监控线程]
2.2 利用停止标记
// 停止标记用 volatile 是为了保证该变量在多个线程之间的可见性// 我们的例子中,即主线程把它修改为 true 对 t1 线程可见class TPTVolatile {private Thread thread; // 采集线程private volatile boolean stop = false; // 线程终止标识位//启动采集功能public void start(){thread = new Thread(() -> {while(true) {Thread current = Thread.currentThread();if(stop) {log.debug("料理后事");break;}try {// 每隔1秒采集、回传一次数据Thread.sleep(1000);log.debug("将结果保存");} catch (InterruptedException e) {// 重新设置线程中断状态// 因为打断sleep中的线程会清空打断状态current.interrupt();}// 执行监控操作}},"监控线程");thread.start();}public void stop() {// 设置中断标识位stop = true;thread.interrupt();}}
调用
TPTVolatile t = new TPTVolatile();t.start();Thread.sleep(3500);log.debug("stop");t.stop();
结果
11:54:52.003 c.TPTVolatile [监控线程] - 将结果保存11:54:53.006 c.TPTVolatile [监控线程] - 将结果保存11:54:54.007 c.TPTVolatile [监控线程] - 将结果保存11:54:54.502 c.TestTwoPhaseTermination [main] - stop11:54:54.502 c.TPTVolatile [监控线程] - 料理后事
案例
JVM内存监控
