楔子
线程终止有两种情况:
- 1、线程的任务执行完成
- 2、线程在执行任务过程中发生异常
这两者属于线程自行终止,如何让线程 A 把线程 B 终止呢?
Java 中 Thread
类有一个stop()
方法,可以终止线程,不过这个方法会让线程直接终止,在执行的任务立即终止,未执行的任务无法反馈,所以 stop()
方法已经不建议使用。
既然 stop()
方法如此粗暴,不建议使用,我们如何优雅地结束线程呢?
正确方法
当线程进入 runnable
状态之后,通过设置一个标识位,线程在合适的时机,检查该标识位,发现符合终止条件,自动退出 run ()
方法,线程终止。
错误实例
一种不靠谱的方式时通过interrupt()
方法结束线程。
线程只有从runnable
状态(可运行/运行状态) 才能进入terminated
状态(终止状态),如果线程处于 blocked、waiting、timed_waiting 状态(休眠状态)
,就需要通过 Thread 类的interrupt()
方法,让线程从休眠状态进入 runnable
状态,从而结束线程。
package com.sino.daily.code_2019_9_1;
/**
* create by 2020-05-07 20:03
*
* @author caogu
*/
public class ThreadInterrupt {
public static void main(String[] args) {
testSystemMonitor();//测试系统监控器
}
/**
* 测试系统监控器
*/
public static void testSystemMonitor() {
SystemMonitor sm = new SystemMonitor();
sm.start();
try {
//运行 10 秒后停止监控
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("监控任务启动 10 秒后,停止...");
sm.stop();
}
}
/**
* 系统监控器
*
* @author ConstXiong
*/
class SystemMonitor {
private Thread t;
/**
* 启动一个线程监控系统
*/
void start() {
t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {//判断当前线程是否被打断
System.out.println("正在监控系统...");
try {
Thread.sleep(3 * 1000L);//执行 3 秒
System.out.println("任务执行 3 秒");
System.out.println("监控的系统正常!");
System.out.println();
} catch (InterruptedException e) {
System.out.println("任务执行被中断...");
}
}
});
t.start();
}
void stop() {
t.interrupt();
}
}
从代码和执行结果我们可以看出,系统监控器start()
方法会创建一个线程执行监控系统的任务,每个任务查询系统情况需要 3 秒钟,在监控 10 秒钟后,主线程向监控器发出停止指令。
但是结果却不是我们期待的,10 秒后并没有终止了监控器,任务还在执行。
原因在于,t.interrupt()
方法让处在休眠状态的语句Thread.sleep(3 * 1000L)
; 抛出异常
,同时被捕获,此时 JVM 的异常处理会清除线程的中断状态
,导致任务一直在执行。
上面问题一种简单处理办法是,在捕获异常后,继续重新设置中断状态,代码如下
package com.sino.daily.code_2019_9_1;
/**
* create by 2020-05-07 20:03
*
* @author caogu
*/
public class ThreadInterrupt {
public static void main(String[] args) {
testSystemMonitor();//测试系统监控器
}
/**
* 测试系统监控器
*/
public static void testSystemMonitor() {
SystemMonitor sm = new SystemMonitor();
sm.start();
try {
//运行 10 秒后停止监控
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("监控任务启动 10 秒后,停止...");
sm.stop();
}
}
/**
* 系统监控器
*
* @author ConstXiong
*/
class SystemMonitor {
private Thread t;
/**
* 启动一个线程监控系统
*/
void start() {
t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {//判断当前线程是否被打断
System.out.println("正在监控系统...");
try {
Thread.sleep(3 * 1000L);//执行 3 秒
System.out.println("任务执行 3 秒");
System.out.println("监控的系统正常!");
System.out.println();
} catch (InterruptedException e) {
System.out.println("任务执行被中断...");
//重新设置线程为中断状态
Thread.currentThread().interrupt();
}
}
});
t.start();
}
void stop() {
t.interrupt();
}
}
到这里还没有结束,我们用Thread.sleep(3 * 1000L)
; 去模拟任务的执行,在实际情况中,一般是调用其他服务的代码,如果出现其他异常情况没有成功设置线程的中断状态,线程将一直执行下去,显然风险很高。所以,需要用一个线程终止的标识
来代替 Thread.currentThread().isInterrupted(
)。
正确实例
用一个线程终止的标识
来代替 Thread.currentThread().isInterrupted(
)
package com.sino.daily.code_2019_9_1;
/**
* create by 2020-05-07 20:03
*
* @author caogu
*/
public class ThreadInterrupt {
public static void main(String[] args) {
testSystemMonitor();//测试系统监控器
}
/**
* 测试系统监控器
*/
public static void testSystemMonitor() {
SystemMonitor sm = new SystemMonitor();
sm.start();
try {
//运行 10 秒后停止监控
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("监控任务启动 10 秒后,停止...");
sm.stop();
}
}
/**
* 系统监控器
*
* @author ConstXiong
*/
class SystemMonitor {
private Thread t;
private volatile boolean stop = false;
/**
* 启动一个线程监控系统
*/
void start() {
t = new Thread(() -> {
while (!stop) {//判断当前线程是否被打断
System.out.println("正在监控系统...");
try {
Thread.sleep(3 * 1000L);//执行 3 秒
System.out.println("任务执行 3 秒");
System.out.println("监控的系统正常!");
} catch (InterruptedException e) {
System.out.println("任务执行被中断...");
Thread.currentThread().interrupt();//重新设置线程为中断状态
}
}
});
t.start();
}
void stop() {
stop = true;
t.interrupt();
}
}