美文网首页
java中使用interrupt通知线程停止

java中使用interrupt通知线程停止

作者: 分布式与微服务 | 来源:发表于2023-02-09 09:20 被阅读0次

    使用 interrupt 来通知线程停止运行,而不是强制停止!

    普通情况停止线程

    public class RightWayStopThreadWithoutSleep implements Runnable {
    
        @Override
        public void run() {
            int num = 0;
            while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
                if (num % 10000 == 0) {
                    System.out.println(num + "是1W的倍数");
                }
                num++;
            }
            System.out.println("任务运行结束!");
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
            thread.start();
            // 等待1s
            Thread.sleep(1000);
            // 通知停止线程
            thread.interrupt();
        }
    }
    

    使用 thread.interrupt() 通知线程停止
    但是 线程需要配合:
    在 while 中使用 Thread.currentThread().isInterrupted() 检测线程当前的状态
    运行结果:

    ……
    ……
    221730000是1W的倍数
    221740000是1W的倍数
    221750000是1W的倍数
    221760000是1W的倍数
    221770000是1W的倍数
    221780000是1W的倍数
    221790000是1W的倍数
    221800000是1W的倍数
    任务运行结束!
    
    Process finished with exit code 0
    

    在可能被阻塞情况下停止线程

    public class RightWayStopThreadWithSleep {
        public static void main(String[] args) throws InterruptedException {
            Runnable runnable = () -> {
                int num = 0;
                while (num <= 300 && !Thread.currentThread().isInterrupted()) {
                    if (num % 100 == 0) {
                        System.out.println(num + "是100的倍数");
                    }
                    num++;
                }
                try {
                    // 等个1秒,模拟阻塞
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("线程已停止!!");
                    e.printStackTrace();
                }
            };
    
            Thread thread = new Thread(runnable);
            thread.start();
            // 等待时间要小于上面设置的1秒,不然线程都运行结束了,才执行到下面的thread.interrupt();代码
            Thread.sleep(500);
            // 通知停止线程
            thread.interrupt();
        }
    }
    

    线程在sleep 1秒的过程中,收到interrupt信号被打断,
    线程正在sleep过程中响应中断的方式就是抛出 InterruptedException 异常
    运行结果:

    0是100的倍数
    100是100的倍数
    200是100的倍数
    300是100的倍数
    线程已停止!!
    java.lang.InterruptedException: sleep interrupted
        at java.lang.Thread.sleep(Native Method)
        at stopthreads.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:19)
        at java.lang.Thread.run(Thread.java:748)
    
    Process finished with exit code 0
    

    在每次迭代后都阻塞的情况下停止线程

    public class RightWayStopThreadWithSleepEveryLoop {
        public static void main(String[] args) throws InterruptedException {
            Runnable runnable = () -> {
                int num = 0;
                try {
                    while (num <= 10000) {
                        if (num % 100 == 0) {
                            System.out.println(num + "是100的倍数");
                        }
                        num++;
                        // 每次循环都要等待10毫秒,模拟阻塞
                        Thread.sleep(10);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
    
            Thread thread = new Thread(runnable);
            thread.start();
            // 5秒后通知停止线程
            Thread.sleep(5000);
            thread.interrupt();
        }
    }
    

    当每次迭代都会让线程阻塞一段时间的时候,在 while/for 循环条件判断时,
    是不需要使用 *Thread.currentThread().isInterrupted() *判断线程是否中断的
    运行结果:

    0是100的倍数
    100是100的倍数
    200是100的倍数
    300是100的倍数
    400是100的倍数
    java.lang.InterruptedException: sleep interrupted
    

    如果将上述代码中的 try/catch 放在 while 循环内

    public class RightWayStopThreadWithSleepEveryLoop {
        public static void main(String[] args) throws InterruptedException {
            Runnable runnable = () -> {
                int num = 0;
                while (num <= 10000) {
                    if (num % 100 == 0) {
                        System.out.println(num + "是100的倍数");
                    }
                    num++;
                    try {
                        // 每次循环都要等待10毫秒,模拟阻塞
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
    
            Thread thread = new Thread(runnable);
            thread.start();
            // 5秒后通知停止线程
            Thread.sleep(5000);
            thread.interrupt();
        }
    }
    

    运行结果:

    0是100的倍数
    100是100的倍数
    200是100的倍数
    300是100的倍数
    400是100的倍数
    java.lang.InterruptedException: sleep interrupted
        at java.lang.Thread.sleep(Native Method)
        at stopthreads.RightWayStopThreadWithSleepEveryLoop.lambda$main$0(RightWayStopThreadWithSleepEveryLoop.java:18)
        at java.lang.Thread.run(Thread.java:748)
    500是100的倍数
    600是100的倍数
    700是100的倍数
    ……
    ……
    
    

    会发现虽然抛出了异常,但是程序并没有停止,还在继续输出,
    即使在 while 条件判断处添加 !Thread.currentThread().isInterrupted() 条件,依然不能停止程序!
    原因是
    java语言在设计 sleep() 函数时,有这样一个理念:
    就是当它一旦响应中断,便会把 interrupt 标记位清除。
    也就是说,虽然线程在 sleep 过程中收到了 interrupt 中断通知,并且也捕获到了异常、打印了异常信息,
    但是由于 sleep 设计理念,导致 Thread.currentThread().isInterrupted() 标记位会被清除,
    所以才会导致程序不能退出。
    这里如果要停止线程,只需要在 catch 内 再调用一次 interrupt(); 方法

    try {
        // 每次循环都要等待10毫秒,模拟阻塞
        Thread.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
        Thread.currentThread().interrupt();
    }
    

    所以说,不要以为调用了 interrupt() 方法,线程就一定会停止。

    两种停止线程最佳方法

    1. 捕获了 InterruptedException 之后的优先选择:在方法签名中抛出异常
    public class RightWayStopThreadInProd implements Runnable {
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new RightWayStopThreadInProd());
            thread.start();
            Thread.sleep(1000);
            thread.interrupt();
        }
    
        @Override
        public void run() {
            while (true) {
                System.out.println("go...");
                try {
                    throwInMethod();
                } catch (InterruptedException e) {
                    // 捕获异常,进行保存日志、停止程序等操作
                    System.out.println("stop");
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 如果方法内要抛出异常,最好是将异常抛出去,由顶层的调用方去处理,而不是try/catch
         * 这样调用方才能捕获异常并作出其它操作
         * @throws InterruptedException
         */
        private void throwInMethod() throws InterruptedException {
            Thread.sleep(2000);
        }
    }
    

    如果方法内要抛出异常,最好是将异常抛出去,由顶层的调用方去处理,而不是 try/catch
    这样调用方才能捕获异常并做出其它操作。

    1. 在 catch 中调用 Thread.currentThread().interrupt(); 来恢复设置中断状态
    public class RightWayStopThreadInProd2 implements Runnable {
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new RightWayStopThreadInProd2());
            thread.start();
            Thread.sleep(1000);
            thread.interrupt();
        }
    
        @Override
        public void run() {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("程序运行结束");
                    break;
                }
                reInterrupt();
            }
        }
    
        private void reInterrupt() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                e.printStackTrace();
            }
        }
    }
    

    这里的 if (Thread.currentThread().isInterrupted()) 判断,就是要你的代码有响应中断的能力。

    总结

    调用 interrupt 方法不一定会中断线程
    通知线程停止,线程不会立即停止,而是会在合适的时候停止
    代码要有响应中断的能力

    相关文章

      网友评论

          本文标题:java中使用interrupt通知线程停止

          本文链接:https://www.haomeiwen.com/subject/nmtbartx.html