在 Java 中停止线程的实现方法有以下 3 种:
自定义中断标识符,停止线程。
使用线程中断方法 interrupt() 停止线程。
-
使用 stop()方法 停止线程。
那么如何优雅的停止线程呢?这三个方法中推荐的是使用interrupt()方法,下面详细来说明一下:
1、自定义中断标识符停止线程
自定义中断标识符就是在程序中定义一个变量来决定线程是否要中断执行,具体实现代码如下:
class FlagThreadDemo extends Thread {
// 自定义中断标识符
public volatile boolean isInterrupt = false;
@Override
public void run() {
// 如果为 true -> 中断执行
while (!isInterrupt) {
// 业务逻辑处理
}
}
}
自定义中断标识符的问题在于:线程中断的不够及时。因为线程在执行过程中,无法调用 while(!isInterrupt) 来判断线程是否为终止状态,它只能在下一轮运行时判断是否要终止当前线程,所以它中断线程不够及时,代码如下:
class InterruptFlagDemo {
// 自定义的中断标识符
private static volatile boolean isInterrupt = false;
public static void main(String[] args) throws InterruptedException {
// 创建可中断的线程实例
Thread thread = new Thread(() -> {
while (!isInterrupt) { // 如果 isInterrupt=true 则停止线程
System.out.println("thread 执行步骤1:线程即将进入休眠状态");
try {
// 休眠 1s
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread 执行步骤2:线程执行了任务");
}
});
thread.start(); // 启动线程
// 休眠 100ms,等待 thread 线程运行起来
Thread.sleep(100);
System.out.println("主线程:试图终止线程 thread");
// 修改中断标识符,中断线程
isInterrupt = true;
}
}
以上代码的执行结果如下图所示:
我们期望的是:线程执行了步骤 1 之后,收到中断线程的指令,线程直接中断,不会再执行步骤 2 了,但从上述执行结果可以看出,使用自定义中断标识符是没办法实现我们预期的结果的,这就是自定义中断标识符,响应不够及时的问题。
2、调用interrupt()方法停止线程
使用 interrupt() 方法可以给执行任务的线程,发送一个中断线程的指令,它并不会直接中断线程,而是发送一个中断线程的信号给要被中断的线程,它把是否中断线程的主动权交给开发人员。开发人员可以通过Thread.currentThread().isInterrupted()判断出当前线程是否收到中断指令,然后再做相应处理,比如说收到中断指令我就停止继续执行相关业务逻辑处理啊,或者我不理会这个中断指令,继续处理相关业务逻辑,亦或者收到中断指令我就去做其他处理,这都是可以的。相比于自定义中断标识符而言,它能更及时的接收到中断指令,如下代码所示:
public static void main(String[] args) throws InterruptedException {
// 创建可中断的线程实例
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("thread 执行步骤1:线程即将进入休眠状态");
try {
// 休眠 1s
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("thread 线程接收到中断指令,执行中断操作");
// 中断当前线程的任务执行
break;
}
System.out.println("thread 执行步骤2:线程执行了任务");
}
});
thread.start(); // 启动线程
// 休眠 100ms,等待 thread 线程运行起来
Thread.sleep(100);
System.out.println("主线程:试图终止线程 thread");
// 修改中断标识符,中断线程
thread.interrupt();
}
以上代码的执行结果如下图所示:
从上述结果可以看出,线程在接收到中断指令之后,立即中断了线程,相比于上一种自定义中断标识符的方法来说,它能更及时的响应中断线程指令。
3、调用stop()方法停止线程
stop()方法虽然可以停止线程,但它已经是不建议使用的废弃方法了,这一点可以通过 Thread 类中的源码发现,stop 源码如下:
从上面的图片可以看出,stop 方法是被 @Deprecated 修饰的不建议使用的过期方法,并且在注释的第一句话就说明了 stop 方法为非安全的方法。在最新版本 Java 中,此方法已经被直接移除了,所以强烈不建议使用。
那为什么废弃stop()方法呢:
因为 stop 方法会直接停止线程,这样就没有给线程足够的时间来处理停止前的保存工作,就会造成数据不完整的问题,因此不建议使用。再举一个例子,比如说你这个线程占用了一把锁,然后你调用stop直接把线程给停掉了,但你这把锁还没有释放,这样就导致了,你这个线程都不存在了,而这把锁却一直被占用着,这样的话,其他线程就会永远都拿不到这把锁。
这里还有一点要说明,stop()方法会释放掉线程占用的Synchronized锁,而不会自动释放ReentrantLock锁,这个主要就是ReentrantLock锁是通过lock()方法加锁,unlock()方法释放锁,而当我们执行stop()方法的时候,很可能就是执行了lock()方法之后,还没执行unlock()方法的时候,执行了stop()方法,这样就导致了ReentrantLock锁没有释放。
总结
本文介绍了停止线程的 3 种方法:
- 自定义中断标识符的停止方法,此方法的缺点是不能及时响应中断请求;
- 使用 interrupt 中断线程方法,此方法是发送一个中断信号给线程,它可以及时响应中断,也是最推荐使用的方法;
- 最后是 stop 方法,但此方法已经是过时的不建议使用的方法,在 Java 最新版本中已经被直接移除了,所以不建议使用。