并发编程之线程中断interrupt

并发编程之线程中断interrupt

线程中断可能在平时的开发中我们用的不多,但是我相信大部分都见过InterruptedException,因为不管我们在调用object.wait()还是Thread.sleep()都会抛出一个InterruptedException。可能有很多人都是直接的继续抛出去或者不做任何处理直接打印堆栈信息,当然有可能这样没有问题,但是有些业务我们这样处理并不适合。要弄懂这些,我们就需要知道interrupt的作用是什么。

一、如何让线程停止

如果我们需要让一个线程停止,我们可以用什么方法呢?我们今天先不讨论那种过时的stop方法,这也不推荐使用,原因是因为太暴力了,它会直接将线程中断,不管你的线程资源是否释放。我在这里描述过我见过的其他两种方式:

  • 自己定义一个volatile的boolean类型的变量控制
  • 使用thread.interrupt

1、使用自定义的volatile的boolean变量控制

package com.taolong.concurrent.interrupt;

/** * @Author * @Version 1.0 * @Description 自己控制线程终端 */
public class MyselfInterruptTest {
   


    public static void main(String[] args) {
   

        MyThread task = new MyThread();

        task.start();

        //先让task跑10秒钟
        try {
   
            Thread.sleep(10000);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
        //终止task
        task.setStop(true);

    }




    static class MyThread extends Thread{
   
        private volatile boolean stop;

        @Override
        public void run() {
   
            while(!stop){
   
                System.out.println("continue run.....");
            }
            System.out.println("--->i will stop...");
        }


        public boolean isStop() {
   
            return stop;
        }

        public void setStop(boolean stop) {
   
            this.stop = stop;
        }
    }
}

2、使用自带的interrupt

package com.taolong.concurrent.interrupt;

/** * @Version 1.0 * @Description 使用interrupt终止 */
public class UseInterruptTest {
   

    public static void main(String[] args) {
   
        Thread task = new Thread(new MyInterruptRunnable());

        task.start();

        //先让task跑10秒钟
        try {
   
            Thread.sleep(10000);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
        //终止task
        task.interrupt();
    }

    static class MyInterruptRunnable implements Runnable{
   

        @Override
        public void run() {
   
            while(!Thread.currentThread().isInterrupted()){
   
                System.out.println("continue run.....");
            }
            System.out.println("--->i will stop...");
        }

    }
}

两个执行的结果是一样的,都是执行10s后终止
在这里插入图片描述

虽然执行的结果相同,但是这只是简单的使用,后面我们继续看一下稍微改变一下代码看看会发生什么变化。

我们现在让任务每隔3s打印一次,修改的代码如下:

public void run() {
   
    while(!Thread.currentThread().isInterrupted()){
   
        System.out.println("continue run.....");
        //每隔3秒打印一次
        try {
   
            Thread.sleep(3000);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
    }
    System.out.println("--->i will stop...");
}

(1)使用自定义的变量控制打印的结果
在这里插入图片描述
(2)使用interrupt控制的打印结果
在这里插入图片描述
这个时候两个打印的结果就不一样了,而且使用interrupt中断的时候,下面并没有中断任务。为什么会产生这样的情况呢?如果不太了解interrupt的使用,是不是容易出错(如果我们真的在这里需要中断业务)。

二、Interrupt使用的正确姿势

前面我们通过两种方法来实现线程中断的操作,可以看出来基本上都能够满足,但是还是有一些差别,尤其是对interrupt的使用,可能会出现跟我们预期的不一致的情况。现在我们就来看看Interrupt到底如何使用。

1、首先需要理解的是interrupt主要是给线程发送一个中断信号,并不会强迫线程立即停止(这也是为什么不使用stop的原因)—理解这一点非常重要

从我们上面的例子可以看出,我们通过在run方法中增加if的逻辑判断去控制线程的中断,如果让我们不加这个if判断的时候,即便我们调用了thread.interrupt()方法,线程也不会中断,而是会继续执行,感兴趣的可以自行编写代码进行测试。

2、线程中断相关的几个方法的介绍和使用interrupt()、isInterrupted()、interrupted()

interrupt():向线程发送一个中断信号标志,至于是否真正中断,要看该线程的具体逻辑

isInterrupted():判断线程是否中断标志,其实就是获取了这个中断标志的值

interrupted():获取了线程的中断信号,但是会清除中断标志位,比如一开始中断标志位为false,后面调用interrupt()将中断标志位设置成true,此时如果调用interrupted()返回的true,但是同时它会将中断标志位置为false,那么下次不管调用isInterrupted()或者是interrupted()返回的都是false

虽然上面三个方法可以控制中断标志位,但是它仅仅是一个变量,它不会直接影响我们的线程的中断。我们必须要自己根据这个变量去控制线程的中断与否。

前面一节的例子中已经说明了interrupt()、isInterrupted()的基本使用,其实可以当作一个基本的boolean类型判断即可。当调用interrupt()产生异常时,则不能将标志位置为true,我们通过代码打印看一下

package com.taolong.concurrent.interrupt;

/** * @Version 1.0 * @Description 使用interrupt终止 */
public class UseInterruptTest {
   

    public static void main(String[] args) {
   
        Thread task = new Thread(new MyInterruptRunnable());

        task.start();

        //先让task跑10秒钟
        try {
   
            Thread.sleep(10000);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
        //发起线程中断信号
        task.interrupt();
    }

    static class MyInterruptRunnable implements Runnable{
   

        @Override
        public void run() {
   
            while(!Thread.currentThread().isInterrupted()){
   
                System.out.println("continue run.....");
                //每隔3秒打印一次
                try {
   
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                    //看看中断标志位结果
                    System.out.println("interrupt=" + Thread.currentThread().isInterrupted());
                }
            }
            System.out.println("--->i will stop...");
        }

    }
}

打印结果:
在这里插入图片描述
从日志中可以看出,中断标志位是false(之前在main方法中的task.interrupt()没生效),如果我们真的需要把正在thread中执行的任务终止掉怎么办呢,可以在catch中调用interrupt()方法,我们试试

catch (InterruptedException e) {
   
    e.printStackTrace();
    //看看中断标志位结果
    System.out.println("interrupt=" + Thread.currentThread().isInterrupted());
    Thread.currentThread().interrupt();
    System.out.println("after invoke interrupt() interrupt=" + Thread.currentThread().isInterrupted());
}

执行结果:
在这里插入图片描述
我们通过在catch中重新设置中断标志位,做到了终止线程的作用,并且中断标志位变成了true

这个时候可能有人会心里想,这比使用自己定义的volatile的boolean类型的变量要麻烦一点,但是仔细思考真的是这样子的吗?—答案显然不是如此,不然jdk也没必要提供interrupt的方法了,比如可以看下面的场景:

1、自定义的volatile的boolean变量能及时相应在阻塞时中断的操作吗(比如当线程正在wait()、sleep()时)
2、自定义的volatile的boolean变量能检测出中断异常、并且根据异常判断任务是否真的需要被中断吗

这个时候发现自定义的变量控制线程中断只能满足一些比较简单的业务,而使用interrupt不仅覆盖了自定义变量的功能,而且还有更强大的功能,所以我们在选择上,建议尽量选择interrupt。

上面还有一个interrupted()方法没有写测试用例,原本想感兴趣的自行测试,在这里也简单的贴一下代码吧

while(!Thread.currentThread().isInterrupted()){
   
    System.out.println("continue run.....");
    //每隔3秒打印一次
    try {
   
        Thread.sleep(3000);
    } catch (InterruptedException e) {
   
        e.printStackTrace();
        //看看中断标志位结果
        System.out.println("interrupt=" + Thread.currentThread().isInterrupted());
        Thread.currentThread().interrupt();//true
        System.out.println("after invoke interrupt() interrupt=" + Thread.currentThread().isInterrupted());
        //使用interrupted(),查看中断标志位
        System.out.println("使用interrupted()判断是否中断 interrupt=" + Thread.interrupted());//返回true,但是会清除,下一次调用为false
        //调用interrupted()后,查看是否会清除标志位
        System.out.println("after invoke interrupted() interrupt=" + Thread.currentThread().isInterrupted());//false
    }
}

打印结果:
在这里插入图片描述
这里对结果稍微解析一下:

1、首先interrupt=false,说明在主线程调用t.interrupt()产生的异常导致并没有中断线程,而且走到了catch

2、当在catch再次调用interrupt()时,表示将中断标注位置为了true

3、当interrupted()判断中断标志位=true—2产生的结果

4、当interrupted()调用后会清除中断标志位=false—-3调用intterupted()清除了标志位

5、此时线程被interrupted()清除后,线程标志位依然是false,while循环判断依然=true,则最终线程未中断

相信大家对interrupt的使用已经比较熟悉了,后面可以根据自己的业务情况判断是否真的需要中断线程哦。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/111207.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 实用的在线网站

    目录文档类1.轻松玩转PDF2.Convertio:在线的文件转换器3.CleverPDF4.vvv文档在线导出工具5.微信公众号格式化编辑器6.ProcessOn:免费在线作图,实时协作7.ALLTOALL(在线格式转换)8.Office-Converter9.SuperTools(依奇在线超级转换工具)10.Ligh…

    2022年4月8日
    65
  • 3D游戏建模真的很累吗?前景怎么样?又需要哪样的基础

    3D游戏建模真的很累吗?前景怎么样?又需要哪样的基础所有行业都是一样的,没有什么容易的,只不过这一行是偏向于技术的,一个有好的建模师月薪10k+是很常见的,这个需要有自己刻苦学习的成果。游戏建模前景在游戏模型行业,你基本不用担心找不到工作,因为游戏模型师人才缺口非常大。举个例子:游戏制作公司的人员配比大多数是这样的:比如100人的三维制作组,可能有60人在做模型贴图,10个人在K动画。只要你保证技能在手,一定是抢手的人才。在几年前游戏建模这个行业不仅仅缺人才,甚至连新手都非常稀缺,那个时候公司愿意招聘实习生,培养他们然后给公司干活,但是工资一定不

    2022年5月19日
    47
  • 哈佛大学《幸福课》笔记

    哈佛大学《幸福课》笔记前述整个九月份以来一直在找工作,工作签好后有各种杂事需要做。当然,更主要的原因是自己懒,所以博客有一个多月没有更新了。今天将自己在大二暑假跟着视频学过的哈佛大学的《幸福课》整理成一篇文章,希望自己以后能够克服困难,多阅读,多学习,多写作,每天能够过的开心充实有意义。《幸福课》笔记(Tal的幸福秘籍)1、你问的问题经常会决定你所做的探索,问题会带来探索。2、信念创造现实,如何理解

    2022年7月25日
    9
  • C#编写单片机上位机软件-串口通讯(BMS汽车动力电池管理系统)

    C#编写单片机上位机软件-串口通讯(BMS汽车动力电池管理系统)上位机软件做了有一段时间,本人呕心沥血的作品。之前用python+pyQT5做,主线程特别特别容易闪退,数据重叠等问题,走了很多弯路;后来用VS的C#做,只能说上手很快,调试效果也不错,而且关键是非常非常简单易读,只希望大家不要像我一样,走这么多弯路–想要源码的小伙伴们,欢迎关注点赞三连+留言邮箱哦~华丽的分割线——————————————————————————————–

    2022年5月31日
    54
  • Eclipse使用教程_eclipse怎么运行整个项目

    Eclipse使用教程_eclipse怎么运行整个项目PS:最近很多同学问我eclipse不知道怎么用,那我们出一个eclipse的教程Start:如果你们觉得eclipse的windows英文版看的很累,我放一个汉化的教程吧👇ht

    2022年8月6日
    6
  • php 手机号正则_正则验证手机号是否合法

    php 手机号正则_正则验证手机号是否合法随着时代的发展,手机号码也在不断扩展。当我在进行PHP网页开发的时候,直接采用在网上找的手机号的正则表达式验证,结果后来测试的时候发现:当我使用的手机号177开头的进行输入时,竟然显示请输入正确的手机号,后来一看正则表达式是没有设定17开头的号码,于是又进行了学习,最终,得到了最新手机号的正则表达式验证(如下代码),以供自己和大家日后学习使用。//$phone存放手机号,$phoneErr存放手机号的错误信息//首先判定手机号不为空,然后进行正则表达式的手机号验证if(!empty($phone))

    2025年12月9日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号