zoukankan      html  css  js  c++  java
  • 如何优雅地停止一个线程?

    线程终止有两种情况:

    1. 线程的任务执行完成
    2. 线程在执行任务过程中发生异常

    这两者属于线程自行终止,如何让线程 A 把线程 B 终止呢?

    Java 中 Thread 类有一个 stop() 方法,可以终止线程,不过这个方法会让线程直接终止,在执行的任务立即终止,未执行的任务无法反馈,所以 stop() 方法已经不建议使用。

    既然 stop() 方法如此粗暴,不建议使用,我们如何优雅地结束线程呢?

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

    当线程进入 runnable 状态之后,通过设置一个标识位,线程在合适的时机,检查该标识位,发现符合终止条件,自动退出 run () 方法,线程终止。

    如我们模拟一个系统监控任务线程,代码如下

    package constxiong.concurrency.a007;
    
    /**
     * 模拟系统监控
     * @author ConstXiong
     */
    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 的异常处理会清除线程的中断状态,导致任务一直在执行。

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

    package constxiong.concurrency.a007;
    
    /**
     * 模拟系统监控
     * @author ConstXiong
     */
    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()。

    修改代码如下

    package constxiong.concurrency.a007;
    
    /**
     * 模拟系统监控
     * @author ConstXiong
     */
    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 秒后,停止...
    任务执行被中断...
    

    到这里基本算是优雅地让线程终止了。


     



     

  • 相关阅读:
    常用排序
    NetBeans 时事通讯(刊号 # 130 Dec 23, 2010)
    《让子弹飞》向我们提出的问题
    NetBeans 时事通讯(刊号 # 130 Dec 23, 2010)
    过去与今天
    过去与今天
    不搞笑不给力——年会小品《山寨新闻联播》
    GAE 博客——B3log Solo 0.2.5 Beta1 发布了!
    EverBox(同步网盘)邀请
    GAE 博客——B3log Solo 0.2.5 Beta1 发布了!
  • 原文地址:https://www.cnblogs.com/ConstXiong/p/11947782.html
Copyright © 2011-2022 走看看