【JUC-Interrupt】中断相关概念

线程中断

  • 一、相关概念
  • 二、API
    • 2.1、isInterrupted方法
    • 2.2、interrupted方法
    • 2.3、interrupt
  • 三、总结:

一、相关概念

一个线程不应该由其他线程中断或停止,应该有线程自己来决定。

在Java中没有办法立即停止一个线程,因此提供了用于停止线程的协商机制中断

Java并没有给中断提供额外的语法,中断过程完全需要程序员自己实现,调用相关API如interrupt,也仅仅是将中断标志位设置为true

以实际例子体验一下:运行一个线程t1,线程t2打断t1的执行。

有三种实现方式:

  • volatilevolatile声明的变量,当值修改之后,其他线程立即可见。
  • AtomicBooleanAtomicBoolean本身就有原子性,不会出现线程安全问题,因此可以用于判断。
  • interrupt

先看volatile实现:

static volatile boolean isStop = false
private static void byVolatile() {new Thread(()->{// 模拟一直工作while (true) {if (isStop) {System.out.println("检测到中断信号,程序停止");break;}System.out.println("working.....");}}, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}new Thread(()->{isStop = true;}, "t2").start();}

AtomicBoolean

static AtomicBoolean stop = new AtomicBoolean(false);
private static void byInterrupt() {var t1 = new Thread(()->{while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println("检测到中断信号,程序停止");break;}System.out.println("working.....");}}, "t1");t1.start();try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}new Thread(()->{t1.interrupt(); // 打断t1}, "t2").start();}

通过默认的interrupt机制:

private static void byInterrupt() {var t1 = new Thread(()->{while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println("检测到中断信号,程序停止");break;}System.out.println("working.....");}}, "t1");t1.start();try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}new Thread(()->{t1.interrupt(); // 在t2线程中打断t1}, "t2").start();}

二、API

中断中有三个比较重要的API:interrupt()isInterrupted()interrupted()
在这里插入图片描述

2.1、isInterrupted方法

源码如下:

    public boolean isInterrupted() {return interrupted;}

注释:
Tests whether this thread has been interrupted. The interrupted status of the thread is unaffected by this method.
测试并获取当前线程的中断状态。

2.2、interrupted方法

    public static boolean interrupted() {Thread t = currentThread();boolean interrupted = t.interrupted;// We may have been interrupted the moment after we read the field,// so only clear the field if we saw that it was set and will return// true; otherwise we could lose an interrupt.if (interrupted) {t.interrupted = false;clearInterruptEvent();}return interrupted;}

源码注释:

Tests whether the current thread has been interrupted. The interrupted status of the thread is cleared by this method. In other words, if this method were to be called twice in succession, the second call would return false (unless the current thread were interrupted again, after the first call had cleared its interrupted status and before the second call had examined it).

简单来说就是:

  1. 如果当前中断标志是true, 那么会返回true并清除当前状态标志,也就是设置为false
  2. 如果当前是false,那么会返回false
  3. 如果在中断标志为true的情况下,连着执行两次,结果依次为truefalse

2.3、interrupt

   public void interrupt() {if (this != Thread.currentThread()) {// Determines if the currently running thread has permission to modify this threadcheckAccess();// thread may be blocked in an I/O operationsynchronized (blockerLock) {Interruptible b = blocker;if (b != null) {interrupted = true;interrupt0();  // inform VM of interruptb.interrupt(this);return;}}}interrupted = true;// inform VM of interruptinterrupt0();}

这段代码上注释挺多的,我们慢慢读:

Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown.

翻译一下:

如果执行这个方法的线程不是本身(因为别的线程也能调用你的这个方法),将会调用checkkAccess方法检查调用者是否有权限调用,没有的话会返回SecurityException

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int) methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

翻译一下,这里很重要:

如果当前线程因为Object中的waitThead中的join、sleep方法阻塞,当调用interrupt方法的时候,会清除当前的标志位,并报InterruptedException 异常。

如果编程的时候没有注意上面这一点,可能没有办法中断线程,引起不可预知的错误。

If this thread is blocked in an I/ O operation upon an InterruptibleChannel then the channel will be closed, the thread’s interrupt status will be set, and the thread will receive a java. nio. channels. ClosedByInterruptException.

翻译:

如果线程因为I/O操作阻塞,调用interrupt将会设置中断位,但是会报一个错误

If this thread is blocked in a java. nio. channels. Selector then the thread’s interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector’s wakeup method were invoked

NIO部分不太清楚,大致意思也是会设置中断位。

If none of the previous conditions hold then this thread’s interrupt status will be set.
Interrupting a thread that is not alive need not have any effect.

翻译一下:

如果没有上述情况发生,线程中断标志位将会被正确设置

中断一个不活动的线程不会产生任何影响

下面用案例演示一下阻塞的时候调用interrupt会出现的问题:

下面是t1线程:

// 线程t1
Thread t1 = new Thread(() -> {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println(Thread.currentThread().getName() + " 中断标志位为:" + Thread.currentThread().isInterrupted() + " 程序停止");break;}try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-------------hello InterruptDemo3");}
}, "t1");
t1.start();try {TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {e.printStackTrace();
}

看到t1的逻辑很简单,如果没有检测到中断标志位,会模拟200ms延迟,然后打印语句。

假如线程启动1s之后,来个t2线程,在执行体中中断了t1, 会不会发生我们上面学到的清空标志位呢?

 new Thread(() -> {t1.interrupt();}, "t2").start();

运行之后会发现,控制台报了异常,但是t1线程没有停止运作,因为在发生异常的时候,interrupt status已经被清除了,导致if 循环检测不到了,程序一直运行下去…

正确的做法是,在Object.wait, Thread.join(), Thread.sleep()catch块中,自己将中断标志位设置为true,防止上面错误:

// 线程t1
Thread t1 = new Thread(() -> {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println(Thread.currentThread().getName() + " 中断标志位为:" + Thread.currentThread().isInterrupted() + " 程序停止");break;}//sleep方法抛出InterruptedException后,中断标识也被清空置为false,如果没有在//catch方法中调用interrupt方法再次将中断标识置为true,这将导致无限循环了try {Thread.sleep(200);} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 自己打断自己e.printStackTrace();}System.out.println("-------------hello InterruptDemo3");}
}, "t1");

三、总结:

方法名作用
isInterrupted返回当前线程的中断标志位 ,注意:如果当前线程是不活跃状态(执行完了),在JDK8中,会自动将中断标志位设置为false,在JDK17中,不会清除中断标志位,也就是说,打断了某个线程,即便是线程执行完毕,调用此方法获得的结果仍旧是true
interrupted静态方法,做两件事情:返回当前中断标志位并清除中断标志位(设为false)
interrupt设置中断标志位为true, 在阻塞的情况下可能会导致无法中断线程的情况,要在catch块中设置中断

解释一下上面的注意

/*** 执行interrupt方法将t1标志位设置为true后,t1没有中断,仍然完成了任务后再结束* 2000毫秒后,t1线程已经结束了,如果是JDK8,会自动恢复为false,JDK17则不会自动恢复标志。*/
public class InterruptDemo2 {public static void main(String[] args) {//实例方法interrupt()仅仅是设置线程的中断状态位为true,不会停止线程Thread t1 = new Thread(() -> {for (int i = 1; i <= 300; i++) {System.out.println("------: " + i);}/*** ------: 298* ------: 299* ------: 300* t1线程调用interrupt()后的中断标志位02:true*/System.out.println("t1线程调用interrupt()后的中断标志位02:" + Thread.currentThread().isInterrupted());}, "t1");t1.start();System.out.println("t1线程默认的中断标志位:" + t1.isInterrupted());//falsetry {TimeUnit.MILLISECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}t1.interrupt();//true/*** ------: 251* ------: 252* ------: 253* t1线程调用interrupt()后的中断标志位01:true*/System.out.println("t1线程调用interrupt()后的中断标志位01:" + t1.isInterrupted());//truetry {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}//2000毫秒后,t1线程已经结束了,如果是JDK8,会自动恢复为false// JDK17则不会自动恢复标志。System.out.println("t1线程调用interrupt()后的中断标志位03:" + t1.isInterrupted());//true (笔者用的JDK17)}
}

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

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

相关文章

直播技术-Android基础框架

目录 &#xff08;一&#xff09;直播间架构 &#xff08;二&#xff09;核心任务调度机制 &#xff08;1&#xff09;复制从滑动直播间加载流程 &#xff08;2&#xff09;核心任务调度机制-代码设计 &#xff08;3&#xff09;核心任务调度机制-接入指南 (三&#xff0…

【es6】原生js在页面上画矩形添加选中状态高亮及显示调整大小控制框(三)

接上篇文章&#xff0c;这篇实现下选中当前元素显示调整大小的控制框&#xff0c;点击document取消元素的选中高亮状态效果。 实现效果 代码逻辑 动态生成控制按钮矩形,并设置响应的css // 动态添加一个调整位置的按钮addScaleBtn(target) {const w target.offsetWidth;con…

ArcGIS应用指南:ArcGIS制作局部放大地图

在地理信息系统&#xff08;GIS&#xff09;中&#xff0c;制作详细且美观的地图是一项重要的技能。地图制作不仅仅是简单地将地理数据可视化&#xff0c;还需要考虑地图的可读性和美观性。局部放大图是一种常见的地图设计技巧&#xff0c;用于展示特定区域的详细信息&#xff…

记录一些PostgreSQL操作

本文分享一些pg操作 查看版本 select version(); PostgreSQL 11.11 查看安装的插件 select * from pg_available_extensions; 查看分词效果 select ‘我爱北京天安门,天安门上太阳升’::tsvector; ‘天安门上太阳升’:2 ‘我爱北京天安门’:1select to_tsvector(‘我爱北京天…

RHCSA作业2

压缩 将整个 /etc 目录下的文件全部打包并用 gzip 压缩成/back/etcback.tar.gz [rootjyh ~]# cd /etc [rootjyh etc]# tar -czf etcback.tar.gz /etc tar: Removing leading / from member names tar: /etc/etcback.tar.gz: file changed as we read it [rootjyh etc]# ls使当…

大语言模型(LLM)安全:十大风险、影响和防御措施

一、什么是大语言模型&#xff08;LLM&#xff09;安全&#xff1f; 大语言模型&#xff08;LLM&#xff09;安全侧重于保护大型语言模型免受各种威胁&#xff0c;这些威胁可能会损害其功能、完整性和所处理的数据。这涉及实施措施来保护模型本身、它使用的数据以及支持它的基…

递推进阶与入门递归

一、递推进阶&#xff0c;勇攀高峰 昆虫繁殖 题目描述 科学家在热带森林中发现了一种特殊的昆虫&#xff0c;这种昆虫的繁殖能力很强。每对成虫过X个月产Y对卵&#xff0c;每对卵要过两个月长成成虫。假设每个成虫不死&#xff0c;第一个月只有一对成虫&#xff0c;且卵长成成虫…

深入浅出:JVM 的架构与运行机制

一、什么是JVM 1、什么是JDK、JRE、JVM JDK是 Java语言的软件开发工具包&#xff0c;也是整个java开发的核心&#xff0c;它包含了JRE和开发工具包JRE&#xff0c;Java运行环境&#xff0c;包含了JVM和Java的核心类库&#xff08;Java API&#xff09;JVM&#xff0c;Java虚拟…

极客大挑战2024wp

极客大挑战2024wp web 和misc 都没咋做出来&#xff0c;全靠pwn✌带飞 排名 密码学和re没做出几个&#xff0c;就不发了 web ez_pop 源代码 <?php Class SYC{public $starven;public function __call($name, $arguments){if(preg_match(/%|iconv|UCS|UTF|rot|quoted…

C++设计模式-策略模式-StrategyMethod

动机&#xff08;Motivation&#xff09; 在软件构建过程中&#xff0c;某些对象使用的算法可能多种多样&#xff0c;经常改变&#xff0c;如果将这些算法都编码到对象中&#xff0c;将会使对象变得异常复杂&#xff1b;而且有时候支持不使用的算法也是一个性能负担。 如何在运…

【初阶数据结构和算法】leetcode刷题之设计循环队列

文章目录 一、实现循环队列1.大致思路分析2.循环队列的结构定义和初始化结构定义初始化 3.循环队列的判空和判满判空和判满难点分析判空判满 4.循环队列的入队列和出队列入队列出队列 5.循环队列取队头和队尾元素取队头元素取队尾元素 6.循环队列的销毁7.最后题解源码 一、实现…

【网络通信】数据集合集!

本文将为您介绍经典、热门的数据集&#xff0c;希望对您在选择适合的数据集时有所帮助。 1 RITA 更新时间&#xff1a;2024-11-22 访问地址: GitHub 描述&#xff1a; RITA 是一个用于网络流量分析的开源框架。 该框架以 TSV 或 JSON 格式提取 Zeek 日志&#xff0c;目前支…

.net core MVC入门(一)

文章目录 项目地址一、环境配置1.1 安装EF core需要包1.2 配置数据库连接二、使用EF创建表2.1 整体流程梳理2.1 建表详细流程三、添加第一个视图3.1整体流程梳理3.1 添加视图,并显示在web里四、使用EF增加Catogory数据,并且读取数据到页面4.1整体流程梳理4.2 实现五、增加Cat…

蓝桥杯不知道叫什么题目

小蓝有一个整数&#xff0c;初始值为1&#xff0c;他可以花费一些代价对这个整数进行变换。 小蓝可以花贵1的代价将教数增加1。 小蓝可以花费3的代价将整数增加一个值,这个值是整数的数位中最大的那个(1到9) .小蓝可以花费10的代价将整数变为原来的2倍, 例如&#xff0c;如果整…

css效果

css炫彩流光圆环效果 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><style>*{margin: 0;padding: 0;}body{width: 100%;height: 100vh;}.container{position: relative;width: 100%;height: 100vh…

提供html2canvas+jsPDF将HTML页面以A4纸方式导出为PDF后,内容分页时存在截断的解决思路

前言 最近公司有个系统要做一个质量报告导出为PDF的需求&#xff0c;这个报表的内容是固定格式&#xff0c;但是不固定内容多少的&#xff0c;网上找了很多资料&#xff0c;没有很好的解决我的问题&#xff0c;pdfmakde、还有html2CanvasjsPDF以及Puppeteer无头浏览器的方案都不…

【C++动态规划 子集状态压缩】2002. 两个回文子序列长度的最大乘积|1869

本文涉及知识点 C动态规划 位运算、状态压缩、枚举子集汇总 LeetCode2002. 两个回文子序列长度的最大乘积 给你一个字符串 s &#xff0c;请你找到 s 中两个 不相交回文子序列 &#xff0c;使得它们长度的 乘积最大 。两个子序列在原字符串中如果没有任何相同下标的字符&…

鸿蒙NEXT开发案例:字数统计

【引言】 本文将通过一个具体的案例——“字数统计”组件&#xff0c;来探讨如何在鸿蒙NEXT框架下实现这一功能。此组件不仅能够统计用户输入文本中的汉字、中文标点、数字、以及英文字符的数量&#xff0c;还具有良好的用户界面设计&#xff0c;使用户能够直观地了解输入文本…

[极客大挑战 2019]BabySQL--详细解析

信息搜集 进入界面&#xff1a; 输入用户名为admin&#xff0c;密码随便输一个&#xff1a; 发现是GET传参&#xff0c;有username和password两个传参点。 我们测试一下password点位能不能注入&#xff1a; 单引号闭合报错&#xff0c;根据报错信息&#xff0c;我们可以判断…

C++《二叉搜索树》

在初阶数据结构中我学习了树基础的概念以及了解了顺序结构的二叉树——堆和链式结构二叉树该如何实现&#xff0c;那么接下来我们将进一步的学习二叉树&#xff0c;在此会先后学习到二叉搜索树、AVL树、红黑树&#xff1b;通过这些的学习将让我们更易于理解后面set、map、哈希等…