如何停止一个线程

参考答案

1.  如何让线程 A 终止线程 B

  • Java 中 Thread 类有一个 stop() 方法,可以终止线程,但是该方法会让线程直接终止,在执行的任务立即终止,未执行的任务无法反馈,因此不建议使用stop() 方法。

2.  有没有更好的方式结束线程

  • 线程只有从 runnable 状态(可运行/运行状态),才能进入terminated 状态(终止状态)。
  • 如果线程处于 blocked、waiting、timed_waiting 状态(休眠状态),就需要通过 Thread 类的 interrupt()  方法,让线程从休眠状态进入 runnable 状态,从而结束线程。
  • 当线程进入 runnable 状态之后,通过设置一个标识位,线程在合适的时机,检查该标识位,发现符合终止条件,自动退出 run () 方法,线程终止。

模拟一个系统监控任务线程:

public class TestSystemMonitor {
    
    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("监控的系统正常!");
                } catch (InterruptedException e) {
                    System.out.println("任务执行被中断...");
                }
            }
        });
        t.start();
    }

    void stop() {
        t.interrupt();
    }
}

结果:

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

看下代码和执行结果,系统监控器 start() 方法会创建一个线程执行监控系统的任务,每个任务查询系统情况需要 3 秒钟,在监控 10 秒钟后,主线程向监控器发出停止指令。

但是结果却不是我们期待的,10 秒后并没有终止了监控器,任务还在执行

原因在于,t.interrupt() 方法让处在休眠状态的语句 Thread.sleep(3 * 1000L);抛出异常,同时被捕获,此时 JVM 的异常处理会清除线程的中断状态,导致任务一直在执行。

处理办法是,在捕获异常后,继续重新设置中断状态

public class TestSystemMonitor {
    
    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("监控的系统正常!");
                } catch (InterruptedException e) {
                    System.out.println("任务执行被中断...");
                    Thread.currentThread().interrupt();//重新设置线程为中断状态
                }
            }
        });
        t.start();
    }

    void stop() {
        t.interrupt();
    }
}

结果:

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

然而,还没有结束。

我们用 Thread.sleep(3 * 1000L); 去模拟任务的执行,在实际情况中,一般是调用其他服务的代码,如果出现其他异常情况没有成功设置线程的中断状态,线程将一直执行下去,显然风险很高。

所以,需要用一个线程终止的标识来代替 Thread.currentThread().isInterrupted()。

修改代码如下:

public class TestSystemMonitor {
    
    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();
    }
}

结果:

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

线程终止。

以上,是Java面试题【如何停止一个线程】的参考答案。

 

输出,是最好的学习方法

欢迎在评论区留下你的问题、笔记或知识点补充~

—end—

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧