java中使用interrupt通知线程停止详析

 更新时间:2022年09月23日 09:22:35   作者:_灯火阑珊处  
这篇文章主要介绍了java中使用interrupt通知线程停止详析,文章介绍的是使用interrupt来通知线程停止运行,而不是强制停止,详细内容需要的小伙伴可以参考一下

前言:

使用 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

这样调用方才能捕获异常并做出其它操作。

2. 在 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通知线程停止详析的文章就介绍到这了,更多相关java interrupt 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中利用gson解析Json实例教程

    Java中利用gson解析Json实例教程

    这篇文章主要给大家介绍了关于Java中利用gson解析Json 的相关资料,文中给出了详细的示例代码供大家参考学习,相信对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-05-05
  • SpringBoot+Vue项目新手快速入门指南

    SpringBoot+Vue项目新手快速入门指南

    最近刚刚做了一个基于vue+springboot的系统,于是基于这点,对遇到的一些问题进行一些配置的汇总,下面这篇文章主要给大家介绍了关于SpringBoot+Vue项目新手快速入门的相关资料,需要的朋友可以参考下
    2022-06-06
  • Java实现五子棋游戏的完整代码

    Java实现五子棋游戏的完整代码

    这篇文章主要为大家详细介绍了Java实现五子棋游戏的完整代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-10-10
  • Java数据结构之数组(动力节点之Java学院整理)

    Java数据结构之数组(动力节点之Java学院整理)

    这篇文章主要介绍了Java数据结构之数组(动力节点之Java学院整理)的相关资料,包括创建和内存分配,数组封装后的使用等,需要的朋友参考下吧
    2017-04-04
  • Java 是如何利用接口避免函数回调的方法

    Java 是如何利用接口避免函数回调的方法

    本篇文章主要介绍了Java 是如何利用接口避免函数回调的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-02-02
  • Java面试必考之如何在项目中优雅的抛出异常

    Java面试必考之如何在项目中优雅的抛出异常

    这篇文章主要为大家详细介绍了Java中的几种异常关键字和异常类相关知识,本文比较适合刚入坑Java的小白以及准备秋招的大佬阅读,需要的可以收藏一下
    2023-06-06
  • Flink开发IDEA环境搭建与测试的方法

    Flink开发IDEA环境搭建与测试的方法

    这篇文章主要介绍了Flink开发IDEA环境搭建与测试的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • 详谈异步log4j2中的location信息打印问题

    详谈异步log4j2中的location信息打印问题

    这篇文章主要介绍了详谈异步log4j2中的location信息打印问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 解释为什么Java中“1000==1000”为false而”100==100“为true

    解释为什么Java中“1000==1000”为false而”100==100“为true

    在日常编程中,我们经常遇到一些看似简单却隐藏着复杂逻辑的问题,这篇文章主要介绍了解释为什么Java中“1000==1000”为false而”100==100“为true,需要的朋友可以参考下
    2024-01-01
  • 详解Java8合并两个Map中元素的正确姿势

    详解Java8合并两个Map中元素的正确姿势

    这篇文章主要介绍了详解Java8合并两个Map中元素的正确姿势,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09

最新评论