文章目录
- 1. 错误思路
- 2. 两阶段终止模式
- 2.1 利用 isInterrupted
- 2.2 利用停止标记
- interrupt-打断park
Two Phase Termination
在一个线程 T1 中如何“优雅”终止线程 T2?这里的【优雅】指的是给 T2 一个料理后事的机会。
1. 错误思路
- 使用线程对象的 stop() 方法停止线程
stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,
其它线程将永远无法获取锁 - 使用 System.exit(int) 方法停止线程
目的仅是停止一个线程,但这种做法会让整个程序都停止
2. 两阶段终止模式
2.1 利用 isInterrupted
interrupt 可以打断正在执行的线程,无论这个线程是在 sleep,wait,还是正常运行
package cn.itcast.test;import lombok.extern.slf4j.Slf4j;@Slf4j(topic = "c.TwoPhaseTermination")
public class Test13 {public static void main(String[] args) throws InterruptedException {TwoPhaseTermination tpt = new TwoPhaseTermination();tpt.start();tpt.start();tpt.start();/*Thread.sleep(3500);log.debug("停止监控");tpt.stop();*/}
}@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination {// 监控线程private Thread monitorThread;// 停止标记private volatile boolean stop = false;// 判断是否执行过 start 方法private boolean starting = false;// 启动监控线程public void start() {synchronized (this) {if (starting) { // falsereturn;}starting = true;}monitorThread = new Thread(() -> {while (true) {Thread current = Thread.currentThread();// 是否被打断if (stop) {log.debug("料理后事");break;}try {Thread.sleep(1000);log.debug("执行监控记录");} catch (InterruptedException e) {}}}, "monitor");monitorThread.start();}// 停止监控线程public void stop() {stop = true;monitorThread.interrupt();}
}
调用
TPTInterrupt t = new TPTInterrupt();
t.start();
Thread.sleep(3500);
log.debug("stop");
t.stop();
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 {Thread.sleep(1000);log.debug("将结果保存");} catch (InterruptedException e) {}// 执行监控操作}},"监控线程");thread.start();}public void stop() {stop = true;thread.interrupt();}
}
interrupt-打断park
package cn.itcast.test;import lombok.extern.slf4j.Slf4j;import java.util.concurrent.locks.LockSupport;import static cn.itcast.n2.util.Sleeper.sleep;@Slf4j(topic = "c.Test14")
public class Test14 {private static void test4() {Thread t1 = new Thread(() -> {for (int i = 0; i < 5; i++) {log.debug("park...");LockSupport.park();log.debug("打断状态:{}", Thread.interrupted()); // interrupted 返回标记后会把打断状态清空,park就可以再次使用了LockSupport.park(); //Thread.currentThread().isInterrupted()不会清空给打断状态,true会使park失效log.debug("2park...");}});t1.start();sleep(1);t1.interrupt();}private static void test3() throws InterruptedException {Thread t1 = new Thread(() -> {log.debug("park...");LockSupport.park();log.debug("unpark...");log.debug("打断状态:{}", Thread.currentThread().isInterrupted());}, "t1");t1.start();sleep(1);t1.interrupt();}public static void main(String[] args) throws InterruptedException {test3();}
}