一篇打通线程 等待 中断

news2024/11/15 19:55:09

✨✨hello,愿意点进来的小伙伴们,你们好呐!
🐻🐻系列专栏:【JavaEE初阶】
🐲🐲本篇内容:介绍 线程的 中断,等待的区别
🐯🐯作者简介:一名现大二的三非编程小白,日复一日,仍需努力。

  • 理解中断 :
  • 自定义Boolean变量中断线程:
  • 线程等待 / 通知机制:
    • wait() 细节:
  • 线程插队现象:

理解中断 :

中断线程 可以理解为 :
在Java线程中有一个布尔类型的标识位属性,这个标识为属性代表着一个正在运行中的线程是否被其他线程进行了中断操作,这个标识符在创建线程的时候是false,代表着该线程未被中断 , 我们可以调用 interrupted() 方法将该标识符置为 true,表示目前这个标识符被重置了,代表着程序被提醒中断 , 在程序中我们都使用 isInterrupted() 来判断该标识符是否为中断标识符.

public class ThreadDemo31 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {//为中断的标识符是false,需要取反才会
                System.out.println("hello");
            }
        });
        thread1.start();
        Thread.sleep(1);
        System.out.println("interrupt前");
        thread1.interrupt();
        System.out.println("interrupt后");
    }
}

在上面的程序中,thread1被启动,然后这个时候标识符属性为false 就取反,让循环进行,然后再main线程休眠1ms后将标识符置为true,表示被中断.

上述的线程中断方法会导致,线程直接中断,但是在某些场景下通知中断后不一定要立刻中断,就比如说:
小明的妈妈通知小明去买酱油,但是小明接收到了通知,但是什么时候去买酱油就取决于小明,妈妈已经通知了,执行权也就在小明这里

那要怎么实现这些需求呢?
在JavaAPI中有许多声明抛出InterruptedException的方法(例如:wait() , join() , sleep()) , 这些方法在抛出InterruptedException 前 Java虚拟机会先将线程的中断标识符清除(置为false),这个时候就是程序接收到中断的信号,但是是否中断取决于程序的代码

接下来来看看代码演示:

public class ThreadDemo31 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {

            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("hello");
            }
        });

        thread1.start();
        Thread.sleep(10000);
        System.out.println("interrupt前");
        thread1.interrupt();
        System.out.println("interrupt后");
    }
}

在这里插入图片描述

我们观察到在调用 interrupt() , 后抛出一个InterruptedException ,然后程序继续执行
我们可以把这些操作分解: 其实就是调用interrupt(),将标识符置为中断状态,然后sleep中休眠中唤醒,唤醒的时候将中断标识符清除,然后再抛出异常,继续执行代码.
那么我们可以在异常这个地方做文章,可以在catch代码块中,直接中断线程,或者等待一段时间后再中断,或者不理会
这个就对应了,接到买酱油的通知后,立刻去买,等一段时间后再去买,不去买 !!!

🚗🚗🚗
下面我来使用代码更加清晰的看出声明了 InterruptedException 的方法对标识符的影响

public class ThreadDemo32 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            while (true) {

            }
        });

        //设置为守护线程,进程结束后一起销毁
        thread1.setDaemon(true);
        thread2.setDaemon(true);

        thread1.start();
        thread2.start();
        Thread.sleep(5000);//让线程充分执行
		
		//通知线程中断
        thread1.interrupt();
        thread2.interrupt();

        System.out.println(thread1.getName() + " " + thread1.isInterrupted());
        System.out.println(thread2.getName() + " " + thread2.isInterrupted());

        System.out.println();
        Thread.sleep(10000);
    }
}

在这里插入图片描述
🚁🚁🚁

我们发现Thread-0 有sleep方法,所以中断标识符就清除了,而Thread-1没有声明了InterruptedException的方法,所以中断标识符保留着

自定义Boolean变量中断线程:

在上文的中断线程中,是使用异常来进行交互的,除了使用标识符导致异常来中断线程外 **,还可以使用一个boolean变量来控制是否要中断线程 **

该程序使用一个静态布尔类型的属性,来控制while循环的条件,从而起到控制线程中断.这种交互方式是安全的,更适合来取消,中断线程执行,但是这种方式一旦启动就无法后悔,只能中断线程

public class ThreadDemo28 {
    static boolean loop = true;
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (loop) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        Thread.sleep(10000);
        loop = false;
    }
}

线程等待 / 通知机制:

在上文中, 我们有使用过 sleep() 来使代码的休眠 , 但是 sleep休眠代码还是有不少缺陷的.
sleep休眠只能来控制休眠时间,无法确保唤醒的及时性 >>
比如说:
我们想睡会觉后就起来上课,但是你不知道上课的时间,所以就无法准确的定闹钟喊醒,只能随意地睡觉,然后随缘起床,最后我们很难保证在上课的时候刚刚好起床,这样子就是无法保证及时性

但是现在有一个Java内置的等待 / 通知 机制可以很好的解决这个问题 , 这个方法存在于java.lang.Object 上
wait() && notify()
在这里插入图片描述
🚗🚗🚗
这个机制指的是:一个线程A通过 调用对象 O 的wait(),进入等待状态,这个时候另一个线程B调用notify()或者notifyAll()来对该线程进行通知,从而通知线程A返回,继续执行

这里的调用wait , notify的对象必须是同一个;
wait调用完的执行过程是,先释放锁,然后将线程进入等待,然后被通知结束等待的时候是要重新加锁
所以该机制都是与synchronized来配套使用的.锁对象与调用wait notify 的对象是必须同一个!

接下来代码来演示一下:

线程Thread1 调用wait(),然后这个时候线程状态为WAITING,然后就死等下去,直到Thread2线程去唤醒Thread1线程,如果这个时候Thread2线程没有去通知Thread1线程,那么这个时候Thread1线程就会进入死等状态,

public class ThreadDemo29 {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        Thread thread1 = new Thread(() -> {
            System.out.println("wait前");
            synchronized (o) {
                try {
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("wait后");
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("notify前");
            synchronized (o) {
                o.notify();
            }
            System.out.println("notify后");
        });
        thread1.start();
        Thread.sleep(1000);//为了thread先执行到等待,避免thread2先执行到notify
        thread2.start();
    }
}

wait() 细节:

💺💺💺
1. 使用wait() , notify() , notifyAll() 要先对调用对象加锁

public class ThreadDemo30 {
    public static void main(String[] args) {
        Object o = new Object();
        Thread thread1 = new Thread(() -> {
            System.out.println("wait前 ");
            try {
                o.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("wait后");
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("notify前");
            o.notify();
            System.out.println("notify后");
        });
        thread1.start();
        thread2.start();
    }
}

没有调用对象加锁就使用 wait() / notify() ,会导致代码执行到wait(),notify()方法就会抛出一个非法锁异常

在这里插入图片描述

2.使用wait() 后 线程状态由RUNNABLE变为WAITING,并把当前线程放置等待队列中去

public class ThreadDemo30 {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        Thread thread1 = new Thread(() -> {
            System.out.println("wait前 ");
            System.out.println(Thread.currentThread().getName() + " " + Thread.currentThread().getState());
            try {
                synchronized (o) {
                    o.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("wait后");
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("notify前");
            System.out.println(thread1.getName() + " " + thread1.getState());
            synchronized (o) {
                o.notify();
            }
            System.out.println("notify后");
        });
        thread1.start();
        Thread.sleep(1000);
        thread2.start();
    }
}

我们很明显地可以看到在没有调用wait()方法前,thread1的状态正在处于RUNNABLE,当调用wait()方法后,调用notify() 方法前,线程thread1的状态变为WAITING

在这里插入图片描述

3.notify() 或 notifyAll() 调用后,等待线程并不会直接从wait() 返回,而是需要等调用notify() 或 notifyAll()的锁释放后,才有机会继续拿到对象锁,从wait() 返回,从wait()方法返回的前提是获得了对象的锁

public class ThreadDemo30 {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        Thread thread1 = new Thread(() -> {
            System.out.println("wait前 ");
            try {
                synchronized (o) {
                    o.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("wait后");
            System.out.println(Thread.currentThread().getName() + " " + Thread.currentThread().getState());
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("notify前");
            synchronized (o) {
                System.out.println(thread1.getName() + " " + thread1.getState());
                o.notify();
                System.out.println("调用notify");
                System.out.println(thread1.getName() + " " + thread1.getState());
            }

            System.out.println("notify后");
        });
        thread1.start();
        Thread.sleep(1000);
        thread2.start();
    }
}

🚂🚂🚂

我们可以看到,在线程1调用wait后,线程1处于WAITING状态,然后线程2调用notify通知线程1返回,这个时候线程1将从WAITING状态变为BLOCKED,因为这个时候线程1被唤醒,但是线程2的锁的对象锁还没有释放,所以线程1这个时候无法获取到对象锁,所以就会阻塞等待,然后最后的RUNNABLE状态就是线程2的锁释放了,被线程1拿到,所以等待线程才有机会从wait返回,最后继续执行,RUNNABLE状态

在这里插入图片描述

4.notify() 是将等待队列中的某一个线程移动到同步队列 从而唤醒, 而notifyAll是将等待队列中的所有线程都移动到同步队列中去, 被移动的线程的状态由 WAITING 变为BLOCKED

这个细节在第三个细节中已经解释了,可以移眼观看噢

🛫🛫🛫
从上述细节中可以看出,等待/通知 机制依托于同步机制,目的是为了确保等待线程从wait()方法返回时可以感知到通知线程对变量做出的修改

线程插队现象:

在我们生活中经常会有插队的现象,其实在线程中也是如此 :join() 方法

🛸🛸🛸
如果一个线程A执行了thread.join() , 表示当前线程A将等待thread线程执行终止后再从thread.join()返回,继续执行

join() 是Thread类的一个静态方法,Thread类除了提供join()之外,还提供了另外具有超时性质的join(long millis) 与 join(long millis,int nanos), 这两个方法表示,如果线程thread在规定时间内没有执行完毕,那么将会从超时方法中返回,继续执行线程A

在这里插入图片描述

接下来使用代码演示一遍:

public class ThreadDemo33 {
    public static void main(String[] args) throws InterruptedException {


        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println(i + "  " + Thread.currentThread().getName());
            }
        });

        thread1.start();

        for (int i = 0; i < 10; i++) {
            if(i == 2){
                thread1.join();
            }
            System.out.println(i + "  " + Thread.currentThread().getName());
        }
    }
}

在这里插入图片描述

当线程main执行到了i == 2 的时候,就让thread1线程先执行,当thread1线程执行完毕后再来执行main线程

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

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

相关文章

思科防火墙——实验练习加命令

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.实验练习 1.实验&#xff08;1&#xff09;配置ASA接口 2.实…

HTML网页制作代码——简约的旅游图文相册博客HTML模板(12页)HTML+CSS+JavaScript 静态HTML旅行主题网页作业

&#x1f468;‍&#x1f393;学生HTML静态网页基础水平制作&#x1f469;‍&#x1f393;&#xff0c;页面排版干净简洁。使用HTMLCSS页面布局设计,web大学生网页设计作业源码&#xff0c;这是一个不错的旅游网页制作&#xff0c;画面精明&#xff0c;排版整洁&#xff0c;内容…

大学生个人网页设计 HTML个人网页制作 web个人网站模板 简单静态HTML个人网页作品

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

[JavaEE]初始操作系统--进程

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录 1. javaEE概述 2. 计算机发展史 3. 冯诺依曼体系 …

Allegro如何移动器件操作指导

Allegro如何移动器件操作指导 Allegro上可以任意移动器件,具体操作如下 选择Edit-move Find选择Symbols Point根据需要选择 Sym Origin是抓取器件的原点 Body center是抓取器件的中心 User Pick可以自定义抓取的原点,在移动整个模块的并且旋转的时候常用的命令 Sym Pin#设…

投稿时要求注册ORCID,这张学术界身份证到底有哪些用处?

您是否经常遇到向国际期刊投稿时需要提供ORCID账号进行登录呢&#xff1f; 又是否在我们看到喜欢的文献时&#xff0c;不免被其作者圈粉&#xff0c;忍不住要去了解这位学者的其他作品和动向时却又很难搜到准确信息&#xff1f; 对中国学者来说&#xff0c;由于投到国际期刊时…

【Matplotlib绘制图像大全】(七):Matplotlib使用xlim()和ylim()修改轴线刻度

前言 大家好,我是阿光。 本专栏整理了《Matplotlib绘制图像大全》,内包含了各种常见的绘图方法,以及Matplotlib各种内置函数的使用方法,帮助我们快速便捷的绘制出数据图像。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmMatp…

Docker-compose部署XWiki

XWiki是一个知识管理平台&#xff0c;用于知识库管理和知识管理。最有名的是Confluence系统&#xff0c;我们现在搭建的是XWiki&#xff0c;这是一个开源系统。 我们可以到官网上查找安装的方法&#xff1a; 这里&#xff0c;我们采用PostgresSQL on Tomcat的方法&#xff1a;…

15.位操作

文章目录位操作15.1二进制数、位和字节15.1.1二进制整数15.1.2有符号整数(?)15.1.3二进制浮点数1.二进制小数2.浮点数表示法15.2其他进制数15.2.1八进制15.2.2十六进制15.3C按位运算符1.二进制反码或按位取反&#xff1a;~2.按位与&#xff1a;&3.按位或&#xff1a;|4.按…

HTML+CSS网页设计期末课程大作业 【茶叶文化网站设计题材】web前端开发技术 web课程设计 网页规划与设计

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

[附源码]计算机毕业设计springboot校园代取快递系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

dreamweaver作业静态HTML网页设计——我的家乡海南旅游网站

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法&#xff0c;如盒子的嵌套、浮动、margin、border、background等属性的使用&#xff0c;外部大盒子设定居中&#xff0c;内部左中右布局&#xff0c;下方横向浮动排列&#xff0c;大学学习的前端知识点和布局方式都有…

LIO-SAM源码解析(五):mapOptmization.cpp

1. 代码流程 1.1. extractSurroundingKeyFrames() 1.2. scan2MapOptimization() 这个函数主要就是进行帧到地图的匹配&#xff0c;通过点到面、点到线的距离距离最小作为优化目标。LOAM中雅阁比矩阵推导其实还是过于复杂了&#xff0c;可以使用进行误差扰动来计算雅阁比矩阵&a…

Unity事件函数的执行顺序

事件函数的执行顺序 脚本生命周期流程图 场景加载时 这些函数在场景开始时被调用&#xff08;场景中的每个对象一次&#xff09;Awake&#xff1a;此函数总是在任何 Start 函数之前以及在预制件被实例化之后调用&#xff08;如果 GameObject 在启动期间处于非活动状态&#xf…

好家伙!阿里并发核心编程宝典(2022版)一夜登顶Github热榜第三

不知道大家今年的金九银十是否有出去面试过&#xff1f;有出去面试的朋友肯定深有感受&#xff0c;像我们刚入行那会面试的加分项现在卷得已经成为了面试的基础题&#xff08;手动狗头&#xff09;。其中最典型的就属这个Java并发编程了。之前一般只有大厂才会有高并发编程相关…

[附源码]Python计算机毕业设计Django基于vuejs的爱宠用品销售app

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

three.js之模型

文章目录简介网格模型旋转平移缩放方法平移旋转属性缩放位置平移旋转复制与克隆复制方法克隆方法注意专栏目录请点击 简介 我们常见的模型有点模型&#xff0c;线模型、网格模型他们的分类如下 点模型 Points对应的材质为点材质PointsMaterial 线模型 Line 线渲染模式Line…

AIGC独角兽官宣联手,支持千亿大模型的云实例发布,“云计算春晚”比世界杯还热闹...

梦晨 发自 凹非寺量子位 | 公众号 QbitAI现在AI最火的方向是什么&#xff1f;那必须是AIGC&#xff08;AI Generated Content&#xff0c;利用人工智能技术来生成内容&#xff09;。AIGC最火的公司是谁&#xff1f;莫过于开源AI作画模型Stable Diffusion背后的StabilityAI。对于…

【数学】焦点弦定理(?)

因为和焦点弦相关的定理太多&#xff0c;我不会把哪一个公式叫成这样的名字&#xff0c;但百度搜出来就是这个名字 如图&#xff0c;椭圆中焦点弦 AB,∣FB∣k∣FA∣(k>0)AB, |FB|k|FA|(k>0)AB,∣FB∣k∣FA∣(k>0) &#xff0c;求 ABABAB 倾斜角的余弦值 cos⁡θ\cos\t…

Anntec ZKUXFT XT2 FGPA卡DPDK使用方法

1. 建议环境 CPU Architecture x86_64、aarch64 CPU MHz&#xff1a; 2000以上 Memory 每个node空闲内存超过2G 硬盘 剩余空间大于100M OS Ubuntu,centos,银河麒麟&#xff0c;UOS等Linux …