你真的了解java线程中断吗?

前言

java.lang.Thread类有一个 interrupt 方法,该方法直接对线程调用。当被interrupt的线程正在sleep或wait时,会抛出 InterruptedException 异常。事实上, interrupt 方法只是改变目标线程的中断状态(interrupt status),而那些会抛出InterruptedException 异常的方法,如wait、sleep、join等,都是在方法内部不断地检查中断状态的值,如果发现中断,则抛出InterruptedException异常。

interrupt方法

Thread实例方法:必须由其它线程获取被调用线程的实例后,进行调用。实际上,只是改变了被调用线程的内部中断状态;

Thread.interrupted方法

Thread类方法:必须在当前执行线程内调用,该方法返回当前线程的内部中断状态,然后清除中断状态(置为false) ;

isInterrupted方法

Thread实例方法:用来检查指定线程的中断状态。当线程为中断状态时,会返回true;否则返回false。

上面的一些说法比较抽象,为了验证上述说法,写几个demo来验证一下。

一、中断和中断检查

1、interrupt方法可能不会中断线程

首先得明确第一个问题:interrupt方法是用于中断线程的方法,但是实际如果线程内没有sleep等阻塞方法,它实际上并不会中断线程,就算有sleep等方法执行,但是如果将异常捕获了,那它也不会中断线程的执行。看以下代码:

public class ThreadTest1 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(new Task("mytask"));t.start();t.interrupt();}static class Task implements Runnable {String name;public Task(String name) {this.name = name;}@Overridepublic void run() {int i = 0;while (true) {System.out.println(i++);}}}
}

运行该程序,将会进入死循环,不断打印i的自增值,最后整型溢出也不会停止,主线程调用的interrupt方法根本无法阻止线程继续运行。

正是之前所说的,“interrupt方法只是改变了被调用线程的内部中断状态“,那如何检查线程的中断状态呢?

2、isInterrupted实例方法检查中断状态

接下来我们调用Thread类的isInterrupted实例方法来检查线程的中断状态

public class ThreadTest2 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(new Task("mytask"));t.start();t.interrupt();}static class Task implements Runnable {String name;public Task(String name) {this.name = name;}   @Overridepublic void run() {//检查两次中断状态,都是trueSystem.out.println("first:"+Thread.currentThread().isInterrupted());System.out.println("second:"+Thread.currentThread().isInterrupted());System.out.println("task " + name + " is over");}}
}

上述代码的运行结果如下

first:true
second:true
task mytask is over

主线程调用了中断方法,线程内调用线程的isInterrupted方法,输出都是true,表示都检测到了中断。为什么要输出两次一模一样的检测结果呢?是为了验证第一次调用的isInterrupted方法并没有改变中断状态。

3、interrupted静态方法检查中断状态

interrupted方法是Thread类的静态方法,它也能检查当前线程的中断状态,但是只能检查一次:这个静态方法有个天坑,它返回中断状态之后,会将中断标志复位成false,所以第二次调用该静态方法就会发现中断标志被改变了。

public class ThreadTest3 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(new Task("mytask"));t.start();t.interrupt();}static class Task implements Runnable {String name;public Task(String name) {this.name = name;}   @Overridepublic void run() {//第一次调用返回中断状态,并将中断状态复位System.out.println("first :" + Thread.interrupted());//第二次调用返回复位后的中断状态System.out.println("second:" + Thread.interrupted());System.out.println("task " + name + " is over");}}
}

中断代码的输出结果为

first :true
second:false
task mytask is over

可以看到,重复调用Thread.interrupted()方法,得到的结果并不一样,原因就是第一次调用的时候中断状态被复位了。

二、中断抛异常的情况讨论

1、阻塞中断并抛出异常

既然是中断方法,那它肯定能在某些情况下中断线程的执行,什么情况下呢?就是在大多数阻塞方法下,比如线程正在sleep、wait、join等。

看下以下代码示例

public class ThreadTest {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(new Task("mytask"));t.start();t.interrupt();}static class Task implements Runnable {String name;public Task(String name) {this.name = name;}@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {System.out.println("thread has been interrupt!");}
//            阻塞情况下中断,抛出异常后线程恢复非中断状态,即 interrupted = falseSystem.out.println("isInterrupted: " +Thread.currentThread().isInterrupted());System.out.println("task " + name + " is over");}}
}

它的运行结果如下

thread has been interrupt!
isInterrupted: false
task mytask is over

线程在sleep期间被中断并抛出了InterruptedException异常,抛出异常后立即重置了中断状态,所以接下来的检查中断方法得到的结果是false

2、interrupted不会中断锁阻塞

对于sleep等阻塞方法,遇到interrupt中断方法会抛出异常,但是对于锁阻塞,则不会,看以下案例

public class ThreadTest4 {public static void main(String[] args) throws InterruptedException {Task mytask = new Task("mytask");Thread t1 = new Thread(mytask);Thread t2 = new Thread(mytask);t1.start();t2.start();t2.interrupt();}static class Task implements Runnable {String name;public Task(String name) {this.name = name;}@Overridepublic void run() {synchronized (this) {System.out.println(new Date() + ":" + Thread.currentThread().getName() + ": start run");try {Thread.sleep(3000L);} catch (InterruptedException e) {System.out.println(new Date() + ":" + Thread.currentThread().getName() + ":catch interrupted exception");}}System.out.println(new Date() + ":" + Thread.currentThread().getName() + ":" + Thread.currentThread().isInterrupted());}}
}

该程序运行结果如下

Fri Jun 14 15:11:52 CST 2024:Thread-0: start run
Fri Jun 14 15:11:55 CST 2024:Thread-1: start run
Fri Jun 14 15:11:55 CST 2024:Thread-0:false
Fri Jun 14 15:11:55 CST 2024:Thread-1:catch interrupted exception
Fri Jun 14 15:11:55 CST 2024:Thread-1:false

线程0持有锁之后等待了3秒钟,在等待期间,线程1尝试进入方法区,但是拿不到锁,所以进不去,进入锁等待状态,这时候主线程调用了线程1的线程中断方法interrupt,但是线程1并没有任何反映,等待线程0释放了锁之后,拿到锁,这时候它开始执行sleep方法,由于线程中断,抛出了InterruptedException,所以它“没睡”,直接打印信息后结束了线程。

如果我们不想线程1抛出异常,该怎么做呢?其实只需要稍微修改一点代码:

System.out.println(new Date() + ":" + Thread.currentThread().getName() + ": start run");

将上面这行代码改成下面这样子

System.out.println(new Date() + ":" + Thread.currentThread().getName() +":" + Thread.interrupted() + ": start run");

只是进入方法区之后加了一点点逻辑:Thread.interrupted()

再次执行上面的主方法,得到的结果为

Fri Jun 14 15:20:09 CST 2024:Thread-0:false: start run
Fri Jun 14 15:20:12 CST 2024:Thread-1:true: start run
Fri Jun 14 15:20:12 CST 2024:Thread-0:false
Fri Jun 14 15:20:15 CST 2024:Thread-1:false

现成1不抛异常了,而且正常等待了3秒钟。。。可见,Thread.interrupted方法真是个隐藏的bug方法啊

三、总结

1、调用interrupt方法中断线程实际上只是设置了中断标志,只有线程在执行sleep、wait等阻塞的方法的时候才会抛出中断异常,但是并不会中断锁阻塞;中断抛出异常后会重置中断状态为false

2、可以调用Thread类的isInterrupted实例方法检测线程的中断状态,该方法不会重置中断状态为false

3、可以调用Thread类的interrupted静态方法检测线程的中断状态,该方法会重置中断状态为false,所以要慎用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/27215.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python】Flask问答系统Demo项目

学习视频 我是跟着知了传课学的Flask,起初了解Flask还是GPT告诉我的,现在可以说用Flask做后端是真的方便! https://www.bilibili.com/video/BV17r4y1y7jJ 项目结构与下载 FlaskOA(项目文件夹) │ app.py │ conf…

web前端黑马下载:探索学习资源的海洋

web前端黑马下载:探索学习资源的海洋 在数字化时代,Web前端技术日益成为互联网行业的核心驱动力。为了跟上这一趋势,众多学习者纷纷投身于Web前端的学习之中。而在这个过程中,“黑马”作为一个备受瞩目的品牌,其Web前…

docker镜像被墙,docker镜像拉不下来 docker镜像拉取失败 如何拉取docker镜像 docker镜像无法提供拉取服务怎么办?最新拉取镜像方式

1. 推荐github仓库提出得解决方案,且每日在更新: https://gist.github.com/y0ngb1n/7e8f16af3242c7815e7ca2f0833d3ea6 2. 我是如何解决的: 用阿里云提供给每个人的镜像,即可解决拉取镜像问题!!&#xff0…

智慧公安指挥中心大数据信息化两中心两基地系统方案

1.1 系统建设目标 本系统是一个汇接全市的报警求助的大型通信指挥系统,技术难度较高、可靠性要求高,技术路线的选择至关重要。 在充分考虑XX市公安局的业务需要,利用现代通信及计算机网络技术的基础上,最大程度地实现资源整合、…

如何开发一款安全高效的Android网络库(详细教程)

根据很多网友及项目需要,我们针对Retrofit做了一层简单封装,包含了很多可插拔的功能,能够适应大多数项目的需要,这一期我们来分析以下如何设计一款安全高效的Android网络库——FlexNet 1. 网络框架模块 在设计网络之前&#xff…

深度学习的舌象诊断:从舌头上了解系统性疾病!

首先 深度学习算法能否解决东方医学中依靠医生经验的诊断问题?而要实现这个目标,需要什么呢? 用舌头诊断被称为口腔健康的指标,但在东方医学中,舌头也被用来评估全身的状况。换句话说,通过分析舌头的图像…

几个阶段性的面试难点整理

一、JVM篇 1、如何排查CPU、内存飙升的问题? 2、是否处理过线上问题?是怎么解决的? 3、谈谈G1收集器对比CMS收集器的优点?什么情况下适合用G1? 4、JVM调优的参数主要指哪方面的调优? 5、堆、栈中分别存放了…

队列 使用链表实现

#include <stdio.h> #include <stdlib.h> // 队列&#xff0c;入队从尾巴入&#xff0c;出队从头出 // 占用第一个位置记录人数 typedef struct Node{int data;struct Node* next; }Node; Node* init_node() {Node* Queue (Node*)malloc(sizeof(Node));Queue->…

【leetcode--单词规律】

题目要求&#xff1a; 跟上一个字符串的思路一致&#xff0c;只是要进行单词的拆分&#xff0c;用.split()函数即可。 class Solution:def wordPattern(self, pattern: str, s: str) -> bool:word s.split()if(len(pattern) ! len(word)):return Falsereturn len(set(patt…

火爆全网《pvz植物大战僵尸杂交版》最新安装包,支持Android、Windows、iOS!

我是阿星&#xff0c;今天跟大家聊聊最近在B站火得一塌糊涂的老游戏——《植物大战僵尸》。你没听错&#xff0c;就是那个曾经让我们熬夜奋战&#xff0c;一关又一关的游戏。 话说回来&#xff0c;这游戏怎么就突然又火起来了呢&#xff1f; 原来&#xff0c;是因为它的最新整…

关于2024年第一批计算机程序设计员(Python)四级、三级职业技能等级证书认证考试的通知

计算机程序设计员&#xff08;Python&#xff09;详细介绍 报名详细信息&#xff1a; 报名截止时间&#xff1a;2024年6月05日 ~ 2024年6月18日 准考证下载时间&#xff1a;2023年6月24日 ~ 6月28日 考试时间&#xff1a;2024年6月29日 四级&#xff1a;08:30~12:30&#…

除了广告和卖货,还有这12种盈利方式

如何让产品赚得更多的钱是永恒的话题&#xff0c;产品的盈利方式也层出不穷。目的是让我们的产品走向商业化&#xff0c;达到变现&#xff0c;不断地更新迭代&#xff0c;发展下去。 本文介绍了产品的12种盈利方式&#xff1a;延迟、便利、图像清晰度、界面、操作速度、灵活性、…

【CS.AL】算法复杂度分析 —— 渐进符号表示法

文章目录 1 概述2 渐进符号详解2.1 大O符号&#xff08;O&#xff09;2.2 Ω符号&#xff08;Ω&#xff09;2.3 Θ符号&#xff08;Θ&#xff09;2.4 o符号&#xff08;o&#xff09;2.5 ω符号&#xff08;ω&#xff09; 3 具体例子3.1 插入排序&#xff08;Insertion Sort…

【论文阅读】《Sketch and Refine: Towards Fast and Accurate Lane Detection》

Abstract 车道检测是指确定道路上车道的精确位置和形状。尽管目前的方法已经做出了努力&#xff0c;但由于现实世界场景的复杂性&#xff0c;这仍然是一项具有挑战性的任务。无论是基于建议的方法还是基于关键点的方法&#xff0c;现有方法都无法有效、高效地描绘车道。基于建…

程序猿大战Python——容器——知识补充

字典遍历方法 目标&#xff1a;了解遍历字典的遍历方法。 当要遍历字典的元素内容&#xff0c;即获取字典的键、值。 常用方法&#xff1a; 函数名含义keys()以列表的形式&#xff0c;返回一个字典所有的键。values()以列表的形式&#xff0c;返回一个字典所有的值。items()返…

腾讯测试开发<ieg 实验室>

3.26 40min 自我介绍实习经历有无遇到什么难点&#xff0c;你是如何克服的在这个项目中你大概做了多少个测试用例&#xff0c;这么多测试用例你平时用什么工具进行管理的&#xff0c;每一次跑全部还是每次只跑一部分现在假设给你一个新的项目&#xff0c;需要你这边去做测试&a…

C++11初始化列表打包器initializer_list

有时我们无法提前知道应该向函数传递几个实参。为了编写能处理不同数量实参的函数我们使用initializer_list Cplusplus中的定义&#xff1a; 其里面有三个成员函数 也就是说他是支持迭代器的&#xff0c;支持迭代器就支持范围for 图像理解 函数类型 void Test1_initializer_li…

在微信小程序中安装和使用vant框架

目录 1、初始化项目2、安装vant相关依赖3、修改 app.json4、修改 project.config.json5、构建npm6、使用示例 本文将详细介绍如何在微信小程序中安装并使用vant框架&#xff5e; 开发工具&#xff1a;微信开发者工具 1、初始化项目 从终端进入小程序项目目录&#xff0c;执行…

JDK8时间类,时区,时间和格式化

一.时间类 二.获取所有的时区 1.获取所有的时区Set<String> zoneIds ZoneId.getAvailableZoneIds();System.out.println(zoneIds.size()); 根据打印的结果可以看到java类中一共有603个时区。 三.获取当前系统默认的时区 ZoneId zoneId ZoneId.systemDefault();Syste…