【Java笔记】多线程:中断

线程中断的作用

线程中断可以使一个线程从等待状态变成就绪状态

使用线程中断,并不是要把线程给终止或是杀死,而是让线程不再继续等待,而是让线程不再继续等待,线程可以继续往下执行代码,线程发生中断后,会抛出一个中断的异常,决定如何处理就看业务代码怎么写

线程的等待状态

详细的JVM线程状态与OS线程状态相关可以看这篇,中断等待一般是从WAITING、TIME WAITING等阻塞,或者说挂起状态(感觉更准确,一般都是主动挂起,而BLOCK阻塞状态一般是竞争锁资源失败被动阻塞到队列中),恢复到RUNNABLE。

WAITING

  • Object.wait():使当前线程处于等待状态,直到另一个线程通过notify()显式唤醒它;
  • Thread.join():插队,当前线程需要等待join的线程执行完毕,底层调用的是Object实例的wait方法;
  • LockSupport.park():除非获得调用许可,否则禁用当前线程进行线程调度。LockSupport.unpark(), 恢复许可来唤醒

TIME_WAITING

  • Thread.sleep(long millis):使当前线程睡眠指定时间

  • Object.wait(long timeout) :线程休眠指定时间,需要与synchronized一起使用,等待期间可以通过notify()/notifyAll()唤醒;

    Object o = new Object();
    synchronized (o){o.wait();//将当前线程挂起,并释放o锁o.notify();//唤醒一个等待在o锁等待队列的线程o.notifyAll();//唤醒等待在o锁等待队列的所有线程
    }
    
  • Thread.join(long millis):等待当前线程最多执行millis毫秒,如果millis为0,则会一直执行;

上述三种方法使线程等待后,可以通过Interrupt()显示唤醒(中断等待

线程从等待中恢复

  • 等待超时:Thread.sleep(long millis) 等到时间了自己回复
  • 得到通知:Object.wait() 线程休眠期间,其他线程可通过notify()/notifyAll()将其唤醒;
  • 使用中断:Interrupt()

java.lang.Thread 中断实现

相关方法

  • **java.lang.Thread.interrupt() :**中断目标线程,给目标线程发一个中断信号,线程被打上中断标记(设为true)。

    public void interrupt() {if (this != Thread.currentThread()) {checkAccess();// thread may be blocked in an I/O operationsynchronized (interruptLock) {Interruptible b = nioBlocker;if (b != null) {interrupted = true;interrupt0();  // inform VM of interruptb.interrupt(this);return;}}}// 打上中断标记interrupted = true;// interrupt0()是一个native方法,主要就是用cpp从OS层面将线程唤醒interrupt0();  // inform VM of interrupt
    }
    
  • **java.lang.Thread.isInterrupted() :**判断目标线程是否被中断,不会清除中断标记。

    public boolean isInterrupted() {return interrupted;
    }
    
  • **java.lang.Thread.interrupted() :**判断目标线程是否被中断,会清除中断标记。

    public static boolean interrupted() {return currentThread().getAndClearInterrupt();
    }// ...
    boolean getAndClearInterrupt() {boolean oldValue = interrupted;// 因为读取interrupted字段时也可能被中断// 此时如果直接清除interrupted,会导致这次新的中断丢失// 所以这里要判断一下if (oldValue) {interrupted = false;clearInterruptEvent(); // native方法}return oldValue;
    }
    

中断标识interrupted

Thread中有一个中断标识属性volatile boolean interrupted ,用于标记当前线程是否中断。有两点要注意:

  • Thread.interrupt()方法做两个工作:
    • 在当前线程将中断标志修改为true,并不是停止线程
    • 通过native的interrupt0()将等待的线程唤醒
  • 如果当前线程在调用Object类的wait()方法或者这个类的join()sleep()方法被打断时,会将它的中断状态将被清除(interrupted设为false),并且它会收到一个InterruptedException异常。

当线程正在等待、休眠或以其他方式被占用,并且线程在活动之前或期间被中断抛出,都会抛出InterruptedException

  • (个人理解)抛出InterruptedException,这个行为是中断的直接结果,捕获这个异常后我们可以让线程执行唤醒后的逻辑(继续运行或者return结束)

    try{Thread.sleep(3000);
    } catch (InterruptedException e) {// 打印日志或者return退出都可以// ...
    }
    // 当前线程的后续逻辑
    // ...
    
  • 线程通过抛出IE从非活跃状态(WAITING、TIMED WAITING)恢复到活跃状态(RUNNABLE)。如果线程本身就是活跃状态,则会无视RUNNABLE,也不会抛出IE。此时如果想处理中断,就需要**isInterrupted()interrupted()**来获取中断状态标识interrupted

一些小练习

Thread.interrupt() 只唤醒线程并修改中断标识

sleep、wait、join挂起线程都会修改中断标识符并抛出InterruptedException,如果没有抛出IE的情况下(比如yield后线程还是RUNNABLE状态)希望响应中断,则需要isInterrupted()interrupted()来获取中断状态标识interrupted

private static void test1(){// 程序中没有响应中断信号的逻辑,线程不会被中断Thread thread = new Thread(()->{System.out.println("线程启动");while (true){Thread.yield();}});thread.start();thread.interrupt();
}
private static void test2(){// 手动响应中断,退出线程Thread thread = new Thread(()->{System.out.println(Thread.currentThread().getName()+": 线程启动");while (true){Thread.yield();// 响应中断if (Thread.currentThread().isInterrupted()){System.out.println(Thread.currentThread().getName()+": 线程被中断");return;}}});thread.start();thread.interrupt();
}

sleep() 清除中断状态标识

private static void test3() throws InterruptedException {// 中断失败,因为sleep会将当前线程的interrupted清楚(设为false)Thread thread = new Thread(()->{System.out.println(Thread.currentThread().getName()+": 线程启动");while (true){// 响应中断if (Thread.currentThread().isInterrupted()){System.out.println(Thread.currentThread().getName()+": 线程被中断,程序退出");return;}try{Thread.sleep(3000);} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName()+": 线程休眠被中断");}}});thread.start();Thread.sleep(2000);thread.interrupt();
}
  • 最开始,子线程start,Thread.currentThread().isInterrupted() 为false,不能进入if(Thread.currentThread().isInterrupted())直接进入try里的sleep(3s)
  • 主线程sleep(2s)
  • 2s后,主线程自己到点唤醒,通过thread.interrupt() 中断唤醒子线程,此时中断标识interrupted为true,由于sleep()抛出InterruptedException(同时将中断标识清除,设为false)被catch捕获,打印“线程休眠被中断”
  • 回到if (Thread.currentThread().isInterrupted()),由于前面sleep抛出IE的同时将中断标识设为false,所以不能执行这个if里的逻辑,结果就是一直在while里重复sleep

在这里插入图片描述

private static void test4() throws InterruptedException {Thread thread = new Thread(() -> {System.out.println(Thread.currentThread().getName()+": 线程启动");while (true){// 响应中断if (Thread.currentThread().isInterrupted()){System.out.println(Thread.currentThread().getName()+": 线程被中断,程序退出");return;}try{Thread.sleep(3000);} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName()+": 线程休眠被中断");Thread.currentThread().interrupt();}}});thread.start();Thread.sleep(2000);thread.interrupt();
}

这里再catch到sleep的IE之后再通过Thread.currentThread().interrupt() 把当前线程的interrupted 表示符置为true,下一个循环就能执行if (Thread.currentThread().isInterrupted()) 的return逻辑了。
在这里插入图片描述
为什么呢?

public static void main(String[] args) {System.out.println(Thread.currentThread().isInterrupted());Thread.currentThread().interrupt();try {System.out.println(LocalTime.now() +": 开始睡眠");System.out.println(Thread.currentThread().isInterrupted());Thread.sleep(3000);} catch (InterruptedException e) {System.out.println(Thread.currentThread().isInterrupted());System.out.println(LocalTime.now()+": 发生中断");}System.out.println(LocalTime.now()+": 结束睡眠");
}

在这里插入图片描述

打印一下中断标识,可以看出sleep之前interrupt()已经将interrupted设为true,sleep会将其改为false并直接抛出IE

Reference

【Java并发·08】线程中断 interrupt
对于Java线程中断的理解,哪种情况下会响应中断?哪种情况下不响应中断?
Java 线程中断?看这篇就够了

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Linux网络编程(二) socket编程及其仿真

本节内容介绍Linux下进行网络编程所必须得socket接口的一些知识 一、socket地址函数 1.1、主机字节序和网络字节序 现代PC大多采用小端字节序,因此小端字节序又被称为主机字节序。 为了避免由于字节序导致的错误,发送端总是将字节序转换为大端字节序…

暖心又实用!母亲节教会妈妈这4招才是最贴心的礼物

母亲节就要到了,这个特殊的日子,我们总是想要为妈妈送上最真挚的祝福和关怀。在这个数字化时代,一部智能手机就能成为我们表达爱意的桥梁。今天,就让我们一起来看看华为手机的四个功能,让妈妈的手机使用体验更加便捷、…

C语言中的关键字static和extern

Hello,亲爱的小伙伴们,我又来了,上一期作者菌讲解了C语言中函数的知识点,得到了很好的反馈,这里作者菌感谢每一个至此我的小伙伴!!今天作者菌又来补充一些很有用的知识,感兴趣的uu们不要吝啬手中…

Jmeter 命令行压测 生成 HTML 测试报告,你真的会?

通常 Jmeter 的 GUI 模式仅用于调试,在实际的压测项目中,为了让压测机有更好的性能,多用 Jmeter 命令行来进行压测。 同时,JMeter 也支持生成 HTML 测试报告, 以便从测试计划中获得图表和统计信息。 以上定义的文件路…

Elementui的el-footer标签使用报错

Elementui的el-footer标签使用报错 其余标签的使用没有报错信息 el-footer的报错信息 原因: ​ 警告信息表示 Vue 不识别 <el-footer> 解决方式: 在组件中进行引入和暴露

Flume 的安装和使用方法

一、Flume的安装 1.下载压缩包 https://www.apache.org/dyn/closer.lua/flume/1.7.0/apache-flume-1.7.0-bin.tar.gz 2.上传到linux中 3.解压安装包 cd #进入加载压缩包目录sudo tar -zxvf apache-flume-1.7.0-bin.tar.gz -C /usr/local # 将 apache-flume-1.7.0-bin.tar.g…

119. 再谈接口幂等性

文章目录 0. 前言1. insert前先select2. 加悲观锁3. 加乐观锁5. 加唯一索引【配合 &#xff08;1. insert前先select &#xff09;最常用 】6. 建防重表6. 根据状态机7. 加分布式锁8. 获取token 0. 前言 在 93. 通用防重幂等设计 一文中&#xff0c;已经介绍过幂等的使用。该文…

力扣:63. 不同路径 II

63. 不同路径 II 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物。那么…

C# Web控件与数据感应之 BaseDataList 类

目录 关于数据感应 BaseDataList 类 范例运行环境 pageview 方法 设计 实现 调用示例 数据源 调用 小结 关于数据感应 数据感应也即数据捆绑&#xff0c;是一种动态的&#xff0c;Web控件与数据源之间的交互&#xff0c;本文将继续介绍以与数据库提取数据并捆绑控件…

CI522/CI523电动车NFC一键启动开发资料

Ci522是一颗工作在13.56MHz频率下的非接触式读写芯片&#xff0c;支持读A卡&#xff08;CI523支持读A/B卡&#xff09;&#xff0c;可做智能门锁、电动车NFC一键启动、玩具NFC开锁等应用。为部分要求低成本&#xff0c;PCB小体积的产品提供了可靠的选择。 Ci522与Si522/MFRC52…

第3章 WebServer重构

3.1 重构原生Web服务框架 3.1.1 分析原生Web服务框架 在服务端代码的 ClientHandler 中&#xff0c;请求解析、处理请求、返回响应的代码混杂在一起&#xff0c;这样的设计会导致代码难以维护和理解。为了提高代码的可读性、可维护性和可扩展性&#xff0c;我们需要对这些代码…

UDP广播

1、UDP广播 1.1、广播的概念 广播&#xff1a;由一台主机向该主机所在子网内的所有主机发送数据的方式 例如 &#xff1a;192.168.3.103主机发送广播信息&#xff0c;则192.168.3.1~192.168.3.254所有主机都可以接收到数据 广播只能用UDP或原始IP实现&#xff0c;不能用TCP…

漏洞挖掘 | EDU证书站任意密码重置

1.前言&#xff1a; 挖了一段时间EDU老破小的站&#xff0c;也该拿证书站下手了。下手的第一个目标&#xff0c;那必然是漏洞排行榜第一的某交大&#xff01;&#xff01;&#xff01; 2.信息搜集 想快速挖到漏洞&#xff0c;必须信息搜集全面。如果信息搜集不到位不全面&…

明星中药企业系列洞察(二)丨百年御药同仁堂,为什么被称为我国最“硬”的老字号?

从最初的同仁堂药室、同仁堂药店到现在的北京同仁堂集团&#xff0c;经历了清王朝由强盛到衰弱、几次外敌入侵、军阀混战到新民主主义革命的历史沧桑&#xff0c;其所有制形式、企业性质、管理方式也都发生了根本性的变化&#xff0c;但同仁堂经历数代而不衰&#xff0c;在海内…

蓝桥杯练习系统(算法训练)ALGO-947 贫穷的城市

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 某城市有n个小镇&#xff0c;编号是1~n。由于贫穷和缺乏城市规划的人才&#xff0c;每个小镇有且仅有一段单向的公路通往别…

[Linux] GDB使用指南----包含CentOS7下安装以及使用

什么是GDB&#xff1f; GDB 是由 GUN 软件系统社区提供的调试工具&#xff0c;同 GCC 配套组成了一套完整的开发环境&#xff0c;GDB 是 Linux 和许多 类Unix系统的标准开发环境。可以用来调试C、C、Go、java、 objective-c、PHP等语言。 GDB的作用 程序启动时&#xff0c;可…

400 Bad Request问题

总结&#xff1a;请求路径写错了 400 问题 原地址&#xff0c;deleteSetmeal的参数应该改为param 更改请求地址正确后即可

视频质量评估

视频质量评估 一、全参考客观视频质量评价方法三、MSSIM四、STRRED五、VMAF六、MOS 一、全参考客观视频质量评价方法 全参考客观视频质量评价方法是指把原始参考视频与失真视频在每一个对应帧中的每一个对应像素之问进行比较。准确的讲&#xff0c;这种方法得到的并不是真正的…

Chromium编译指南2024 Windows11篇-Git工具准备(四)

前言 在《Chromium编译指南2024&#xff08;三&#xff09;》中&#xff0c;我们已经完成了对 Chromium 编译环境的其他相关环境变量的设置&#xff0c; 接下来&#xff0c;我们将进一步探讨如何初始化配置 Git&#xff0c;为获取 Chromium 源代码做好准备。 1. 配置Git 用户…