写点什么

在 Java 中如何优雅的停止一个线程?可别再用 Thread.stop() 了!

  • 2024-03-14
    福建
  • 本文字数:2312 字

    阅读完需:约 8 分钟

写在开头


经过上几篇博文的学习,我们知道在 Java 中可以通过 new Thread().start()创建一个线程,那今天我们就来思考另外一个问题:线程的终止自然终止有两种情况:


1. 线程的任务执行完成;

2. 线程在执行任务过程中发生异常。


start 之后,如果线程没有走到终止状态,我们该如何停止这个线程呢?


为什么 stop 终止不可用


翻看 Thread 源码后,发现其提供过一个 stop()方法,可以用来终止线程,我们看一下它的源码。

【源码解析 1】

@Deprecated    public final void stop() {        SecurityManager security = System.getSecurityManager();        if (security != null) {            checkAccess();            if (this != Thread.currentThread()) {                security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);            }        }        // A zero status value corresponds to "NEW", it can't change to        // not-NEW because we hold the lock.        if (threadStatus != 0) {            resume(); // Wake up thread if it was suspended; no-op otherwise        }         // The VM can handle all thread states        stop0(new ThreadDeath());    }
复制代码


这个方法使用了 @Deprecated 修饰,代表着它是废弃的方法,在 Java 的编码规约中,过时的方法不建议继续使用,并且在这个方法的注释中官方也提示说这是一个不安全的强制恶意中断方法,会破坏线程的原子性。



因此,在这里强烈建议大家不要再用 stop 方法去停止线程了!


如何优雅的停止一个线程


我们知道线程只有从 runnable 状态(可运行/运行状态) 才能进入 terminated 状态(终止状态),如果线程处于 blocked、waiting、timed_waiting 状态(休眠状态),就需要通过 Thread 类的 interrupt() 方法,让线程从休眠状态进入 runnable 状态,从而结束线程。

这里就涉及到了一个概念“线程中断”,这是一种协作机制,当其他线程通知需要被中断的线程后,线程中断的状态被设置为 true,但是具体被要求中断的线程要怎么处理,完全由被中断线程自己决定,可以在合适的时机中断请求,也可以完全不处理继续执行下去,这样一来,安全性就得到了保障。

Thread 类中提供线程中断的方法如下:


  • Thread.interrupt():中断线程。这里的中断线程并不会立即停止线程,而是设置线程的中断状态为 true(默认是 flase);

  • Thread.currentThread().isInterrupted():测试当前线程是否被中断。线程的中断状态会受这个方法的影响,调用一次可以使线程中断状态变为 true,调用两次会使这个线程的中断状态重新转为 false;

  • Thread.isInterrupted():测试当前线程是否被中断。与上面方法不同的是调用这个方法并不会影响线程的中断状态。


Ok,写了那么多,我们来写一个小的 demo 测试一下线程中断的方法。

【代码示例】

public class Test {    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();    }}/*系统监控器*/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("任务执行被中断...");                    //重新设置线程为中断状态,保证JVM抛异常情况下,中断状态仍为true。                    Thread.currentThread().interrupt();                }            }        });        t.start();    }    //线程中断    void stop() {        stop = true;        t.interrupt();    }}
复制代码


在这里我们先创建了一个 SystemMonitor 类作为系统检测器,每 3 秒一循环的进行检测,考虑到在 Thread.currentThread().isInterrupted()可能在某些情况下中断失效,所以我们这里自定义一个 stop 变量,作为线程中断的标识,检测线程启动先对标识位进行判断。

然后,我们在 Test 类中写一个测试方法,调用这个系统监控器,进行检测,并设置 10 秒后,调用 stop 方法中断检测线程,将中断标识 stop 设置为 true。启动代码后,我们在控制台可以看到这样的输出:

正在监控系统...任务执行 3 秒监控的系统正常!正在监控系统...任务执行 3 秒监控的系统正常!正在监控系统...任务执行 3 秒监控的系统正常!正在监控系统...监控任务启动 10 秒后,停止...任务执行被中断...
复制代码


与我们的预期一样,监控线程在执行了 3 个循环的检测任务后,被成功中断调。到这里,我们就成功的、安全的、优雅的停止了一个线程啦!


文章转载自:JavaBuild

原文链接:https://www.cnblogs.com/JavaBuild/p/18070342

体验地址:http://www.jnpfsoft.com/?from=001

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
在Java中如何优雅的停止一个线程?可别再用Thread.stop()了!_Java_快乐非自愿限量之名_InfoQ写作社区