一个线程不断的去记录系统的信息,当用户按下停止按钮时,启动另一个线程去停止记录线程。
如何让一个线程 T1 去停止另一个线程 T2 呢?很简单,Thread 为我们提供了一个 API,就是 stop() 方法。
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("记录系统信息...");
}
}, "t1");
t1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread t2 = new Thread(() -> {
log.info("停止记录监控...");
t1.stop();
}, "t2");
t2.start();
}
使用 stop 方法确实能达到效果,但是却有个致命的问题,当 stop 一个线程时,该线程在哪里被 stop 我们是无法感知的! stop 方法并不稳定,现在已经过时。
可应采取两阶段暂停模式来优雅的停止线程。而两阶段终止模式的关键就在于 interrupt()
方法的使用。
先介绍下 interrupt() 方法。
当调用一个线程的 interrupt 方法时,并不会直接让该线程停止,而是给该线程设置一个打断标记,也就是打断标记为 true
。
但是注意,如果被打断线程正处于 sleep、wait、join这三种状态,则会导致被打断的线程抛出 InterruptedException,并清除打断标记,也就是置为
false
。
那么我们就可以通过判断打断标记是否为真,来停止线程。
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
Thread currentThread = Thread.currentThread();
while (true) {
if (currentThread.isInterrupted()) {
log.info("收尾操作...");
break;
}
try {
// 情况1:睡眠时被打断,打断标记被清空,为 false
Thread.sleep(500);
} catch (InterruptedException e) {
// 打断标记被清空,需要重新打断设置打断标记为 true
currentThread.interrupt();
log.info("是否被打断: {}", currentThread.isInterrupted());
e.printStackTrace();
}
// 情况2:正常运行时被打断,打断标记为 true
log.info("记录系统信息...");
}
}, "t1");
t1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread t2 = new Thread(() -> {
log.info("停止记录监控...");
t1.interrupt();
}, "t2");
t2.start();
}
可以看到,使用两阶段终止模式可以优雅的停止线程。
注:sleep 的睡眠被打断会抛出异常,情况打断标记,因此在 catch 块中,需要重新设置打断标记。