• 【JAVA】给线程的interrupt()方法使用举个栗子


    前言

    进入正题之前还是先回顾一个会被问无数遍的问题:

    • stop和interrupt的区别是什么?
      简单的说就是,stop的话,那线程就真的结束了。
      interrupt仅仅只是给线程标记了一下,告诉你说当前线程可以结束了,你需要自己进行结束线程。如果你自己不做处理,那这个方法调用了并看不出效果。

    栗子一:不带sleep等中断抛异常方法的使用

    上代码:

    public class TestClassSenseOne {
        public static void main(String[] args) throws InterruptedException {
            TestThread t = new TestThread();
    
            //开启子线程执行
            t.start();
    
            //中断子线程
            t.interrupt();
    
            //等待子线程执行完毕,t.join() 可以这么理解,谁.join() 谁阻塞当前线程先执行自己
            t.join();
    
            System.out.println("主线程结束");
        }
    }
    
    class TestThread extends Thread {
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                //没有被标记为中断状态就执行,上面start后直接调用中断,这句是不会输出的
                System.out.println("我没有被中断");
            }
            System.out.println("啊——————————————————————————————断了。");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    运行结果:

    啊——————————————————————————————断了。
    主线程结束
    
    • 1
    • 2

    说明:
    上面这段代码就是,interrupt()的基本用法,子线程TestThread会一直判断当前线程有没有被标记为可以中断了,直到检查到被标记了,while循环结束,子线程任务结束。

    栗子二:带sleep的使用

    public class TestClassSenseTwo {
        public static void main(String[] args) throws InterruptedException {
            
            TestThreadTwo t = new TestThreadTwo();
            //开启子线程执行
            t.start();
    
            //模拟真实任务,其实执行5秒就执行完了,再继续等待到20s毫无意义
            Thread.sleep(5000);
            System.out.println("模拟真实任务,其实执行5秒就执行完了,再继续等待到20s毫无意义");
    
            //所以在这里中断子线程
            t.interrupt();
    
            //等待子线程执行完毕,t.join() 可以这么理解,谁.join() 谁阻塞当前线程先执行自己
            t.join();
    
            System.out.println("主线程结束");
        }
    }
    
    class TestThreadTwo extends Thread {
        @Override
        public void run() {
            try {
                System.out.println("开始执行20秒的任务");
                Thread.sleep(20000);
                System.out.println("线程任务执行完毕");
            } catch (InterruptedException e) {
                System.out.println("线程在sleep的时候被中断。");
                return;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    运行结果:

    开始执行20秒的任务
    模拟真实任务,其实执行5秒就执行完了,再继续等待到20s毫无意义
    线程在sleep的时候被中断。
    主线程结束
    
    • 1
    • 2
    • 3
    • 4

    可以看到,这里了结束子线程的操作输出是在 catch块中做的,因为sleep被中断会抛出InterruptedException异常,同时也会清除当前线程被标记的 可被结束状态,如果不在catch块中结束,那么interrupt的调用并不能提前结束子线程。

    大栗子:场景下使用的例子

    • 描述:
      我在程序启动时有一个检查菜单的任务,这个任务需要一直被执行,直到检查成功。
    • 思路:
      我先定义一个任务线程类,在其中while (!Thread.currentThread().isInterrupted())执行检查菜单方法,当检查菜单方法使用interrupt中断任务时说明任务完成。

    上代码:

    检查方法的Service接口

    public interface TestService {
        void checkMenu() throws InterruptedException;
    }
    
    • 1
    • 2
    • 3

    实现类

    public class TestServiceImpl implements TestService {
    
        @Override
        public void checkMenu() throws InterruptedException {
    
            System.out.println("检查菜单----------->");
            //模拟随机成功与失败
            if (Math.random() * 10 > 4) {
                //模拟执行检查任务需要3秒耗时
                Thread.sleep(3000);
    
                //使用中断的方式,结束任务。
                System.out.println("检查任务完成******************,checkMenu()  使用interrupt()结束任务");
                Thread.currentThread().interrupt();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    任务类

    
    class DoMenuCheckTask extends Thread {
        @Override
        public void run() {
            TestService testService = new TestServiceImpl();
            //如果当前线程没有被中断,说明 checkMenu 没有真正成功
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    testService.checkMenu();
    
                    /*
                      下面这两行的开启与关闭,便是两种结束当前任务的方式
                       1. 这里不使用sleep ,任务正常被 interrupt 结束
                       2. 这里使用 sleep, interrupt打断的就是 sleep(),打断sleep会清除isInterrupted中断状态,
                            并抛出 java.lang.InterruptedException: sleep interrupted 异常,所以,不在catch块中结束任务,
                            任务便无法结束了。
                     */
    //                System.out.println("菜单检查没有通过中断完成任务,休眠5秒后继续执行----------");
    //                //如果这里使用了 sleep 那么任务便是通过下面catch块中结束。
    //                Thread.sleep(5000);
    
                } catch (InterruptedException e) {
                    //被中断,任务结束
                    e.printStackTrace();
                    System.out.println("菜单检查通过中断interrupt成功完成----------->>>>>>>>");
                    return;
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    执行

    public class SceneExample {
        public static void main(String[] args) throws InterruptedException {
            System.out.println("主线程开始执行----------->>>>>>>>");
            DoMenuCheckTask task = new DoMenuCheckTask();
    
            //开始菜单检查执行任务
            task.start();
    
            //让检查任务先执行
            task.join();
    
            System.out.println("主线程结束执行----------->>>>>>>>");
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    不使用sleep输出结果:

    主线程开始执行----------->>>>>>>>
    检查菜单----------->
    检查任务完成******************checkMenu()  使用interrupt()结束任务
    主线程结束执行----------->>>>>>>>
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用sleep的输出结果:

    主线程开始执行----------->>>>>>>>
    检查菜单----------->
    检查任务完成******************checkMenu()  使用interrupt()结束任务
    菜单检查没有通过中断完成任务,休眠5秒后继续执行----------
    菜单检查通过中断interrupt成功完成----------->>>>>>>>
    主线程结束执行----------->>>>>>>>
    java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at com.kusch.demo.test.example.DoMenuCheckTask.run(SceneExample.java:47)
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    挠场的科学丨五、二十一世纪的挠力文明
    JVM常用概念之扁平化堆容器
    JNDI,SPI,JMS(AMQP) , JMX,RMI与RPC
    精选了20个Python实战项目(附源码)
    国产PHY YT8521S UTP/FIBER 模式的一次调试
    Spring整合第三方框架-MyBatis整合Spring实现
    vim批量多行缩进调整
    分析设备树文件
    ArcGIS Engine:C#基础语法的了解
    代码随想录51——动态规划:309最大买卖股票时机含冷冻期、714买卖股票的最大时机含手续费、300最长递增子序列
  • 原文地址:https://blog.csdn.net/qq_38397501/article/details/126181191