【Java笔记】多线程:一些有关中断的理解

news2025/1/15 7:51:49

文章目录

  • 线程中断的作用
    • 线程的等待状态
      • WAITING
      • TIMED_WAITING
    • 线程从等待中恢复
  • `java.lang.Thread`中断实现
    • 相关方法
    • 中断标识`interrupted`
  • 一些小练习
    • `Thread.interrupt()` 只唤醒线程并修改中断标识
    • `sleep()` 清除中断状态标识
  • Reference

线程中断的作用

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

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

线程的等待状态

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

WAITING

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

TIMED_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 operation
            synchronized (interruptLock) {
                Interruptible b = nioBlocker;
                if (b != null) {
                    interrupted = true;
                    interrupt0();  // inform VM of interrupt
                    b.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://www.coloradmin.cn/o/1650385.html

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

相关文章

关于2024年上半年软考考试批次安排的通告

按照《2024年计算机技术与软件专业技术资格(水平)考试工作安排及有关事项的通知》(计考办〔2024〕1号)文件精神,结合各地机位实际,现将2024年上半年计算机软件资格考试有关安排通告如下: 一、考…

鸿蒙内核源码分析(中断管理篇) | 江湖从此不再怕中断

关于中断部分系列篇将用三篇详细说明整个过程. 中断概念篇 中断概念很多,比如中断控制器,中断源,中断向量,中断共享,中断处理程序等等.本篇做一次整理.先了解透概念才好理解中断过程.用海公公打比方说明白中断各个概念…

GORM 与 MySQL(一)

GORM 操作 Mysql 数据库(一) 温馨提示:以下关于 GORM 的使用,是基于 Gin 框架的基础上,如果之前没有了解过 Gin 可能略微困难。 GORM 介绍 GORM 是 Golang 的一个 orm 框架。简单说,ORM 就是通过实例对象…

京东工业优选商品详情API接口:解锁高效工业采购新体验

京东工业优选的商品详情API接口,允许开发者通过程序化的方式,快速获取平台上的商品详细信息。这些详细信息包括但不限于商品名称、价格、规格、库存、图片、评价等,为企业提供全方位的商品信息查询服务。 二、API接口的主要功能 实时查询&a…

如何查看慢查询

4.2 如何查看慢查询 知道了以上内容之后,那么咱们如何去查看慢查询日志列表呢: slowlog len:查询慢查询日志长度slowlog get [n]:读取n条慢查询日志slowlog reset:清空慢查询列表 5、服务器端优化-命令及安全配置 安…

DDR4 新功能介绍

DDR4(第四代双倍数据率同步动态随机存取内存)相较于其前代DDR3,引入了一些新的功能和改进,这些新功能有助于提高内存的性能、降低功耗以及增强系统的可靠性,包括VPP、DBI(Data Bus Inversion,数据总线翻转)和DMI(与LPDDR4相关)。以下是对这些功能的简要说明: 更高的…

Stable Diffusion:AI绘画的新纪元

摘要: Stable Diffusion(SD)作为AI绘画领域的新星,以其开源免费、强大的生成能力和高度的自定义性,正在引领一场艺术与技术的革命。本文旨在为读者提供Stable Diffusion的全面介绍,包括其原理、核心组件、安…

嵌入式学习——C语言基础——day15

1. 段错误调试 1.1 打印法 在可能出现错误的位置加入打印,前一句能够打印出来,后一句打印不出来,问题就可以定位到两次打印中间的代码 1.2 gbd调试法 1. 编译代码时加入-g选项 gcc filename.c -g 2. 使用gdb调试生成的代码 gdb a.out 3. gdb调试命令 l 查看…

突然断电,瀚高数据库启动失败

服务器临时断电后,数据库启动不起来 ps -ef|grep postgres 进到数据库的data目录下看下ls 看下 查看临时文件: ls -la /tmp 把这两个5866的文件改个名字张老师 加个bak就行 改完了pg_ctl start起一下

六、文件查找

一、文件查找 1.查找文件内容 ​ 命令:grep keywords /dir_path/filename 2.查找系统命令 ​ 命令:which command 3.查找命令及配置文件位置 ​ 命令:whereis command 4.find查找 ​ find $find_path -name|-type|-perm|-size|-atime…

HarmonyOS实战开发教程-如何开发一个2048游戏

今天为大家分享的是2048小游戏,先看效果图: 这个项目对于新手友友来说可能有一点难度,但是只要坚持看完一定会有收获。因为小编想分享的并不局限于ArkTs语言,而是编程思想。 这个游戏的基本逻辑是初始化一个4乘4的数组&#xff…

跟TED演讲学英文:4 pillars of college success in science by Freeman Hrabowski

4 pillars of college success in science Link: https://www.ted.com/talks/freeman_hrabowski_4_pillars_of_college_success_in_science Speaker: Freeman Hrabowski Date: February 2013 文章目录 4 pillars of college success in scienceIntroductionVocabularyTranscr…

休斯《公共管理导论》第5版/考研真题解析/章节题库

第一部分 考研真题精选 一、概念题二、简答题三、论述题四、案例分析题第二部分 章节题库 第1章 一个变革的时代第2章 政府的角色第3章 传统的公共行政模式第4章 公共管理第5章 公共政策第6章 治 理第7章 问 责第8章 利害关系人和外部环境第9章 管制、外包和公共企…

有哪些软件可以使用云渲染?

随着技术的发展,云渲染已成为动画制作人员与设计师重要的渲染助手。它可结合云端强大的计算机能力,帮助渲染人员高速的完成渲染任务,大幅度节省时间和本地计算资源。它们以用户友好的界面、强大灵活的渲染能力,满足了各类专业渲染…

鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽

进程间为何要通讯 ? 鸿蒙内核默认支持 64个进程和128个任务,由进程池和任务池统一管理.内核设计尽量不去打扰它们,让各自过好各自的日子, 但大家毕竟在一口锅里吃饭, 不可能不与外界联系, 联系就得有渠道&#xff0c…

【进程终止】退出信号 | 三种退出情况 | 如何进程终止returnexit_exit

目录 退出码 退出信号 进程终止情况3 如何进程终止 return退出 库函数exit 系统调用函数_exit ​exit和_exit的区别缓冲区 exit _exit 退出码 回顾上篇 代码跑完,结果正确(退出码为0)代码跑完,结果不正确(退…

选择器、pxcook软件、盒子模型

结构伪类选择器 定义&#xff1a;根据结构的元素关系来查找元素。 <title>Document</title><style>li:first-child{color:aqua ;}li:last-child{color: aqua;}li:nth-child(3){color: aqua;}</style> </head> <body><ul><li>…

端口被其他进程占用:OSError: [Errno 98] Address already in use

一、问题描述 错误提示端口号正在被使用 二、解决办法 1.使用 lsof 命令&#xff0c;列出所有正在监听&#xff08;即被绑定&#xff09;的网络连接&#xff0c;包括它们所使用的端口号 sudo lsof -i -P -n | grep LISTEN 2.解绑被绑定的端口号 根据 netstat 或 lsof 命令…

C#修改默认参数settings文件

右击项目在设置中进行修改&#xff1a; 千万不要在这里改。 如果要在自己的项目里添加这个文件&#xff0c;首先新建个文件夹&#xff0c;然后添加.setting文件&#xff0c;然后再像上面说的那样添加属性。

Ansible自动化工具模块调用与playbook编写

目录 一、Ansible工作机制与特点 &#xff08;一&#xff09;Ansible工作机制 1. 初始化与配置 2. 编写Playbook 3. 调用模块 4. 加密敏感数据 5. 执行Playbook 6. 收集执行结果 7. 错误处理与回滚 8. 反馈与报告 &#xff08;二&#xff09;Ansible 的主要特点包括…