JavaWeb12-线程通讯(线程等待和唤醒)
目录
1.方法介绍
1.1.wait()/wait(long timeout):让当前线程进入等待状态。
1.1.1.wait执行流程
1.1.2.wait结束等待的条件
1.1.3.wait() VS wait(long timeout)
1.1.4.为什么wait要放在Object中?
--->PS:wait(0) 和 sleep(0) 的区别
--->PS:wait 和 sleep 释放锁行为的区别
--->PS:(常见面试题)wait 和 sleep 的区别(小结)
--->PS:3种方法让线程进入休眠/等待状态
1.2.notify():唤醒当前对象上一个休眠的线程(随机)。
1.3.notifyAll():唤醒当前对象上的所有线程。
2.注意事项:
由于线程之间是抢占式执⾏的,因此线程之间执⾏的先后顺序难以预知。但是实际开发中有时候希望合理地协调多个线程之间的执⾏先后顺序。
球场上的每个运动员都是独⽴的 "执⾏流",可以认为是⼀个 "线程"。⽽完成⼀个具体的进攻得分动作,则需要多个运动员相互配合, 按照⼀定的顺序执⾏⼀定的动作,线程1 先 "传球",线程2 才能 "扣篮"。
1.方法介绍
完成这个协调⼯作(线程通讯),主要涉及到以下三个⽅法。注意这三个方法都是对象级别的(需要通过"对象."来调用),【不是锁级别,如Thread.sleep()】都是Object类的内置方法。
1.1.wait()/wait(long timeout):让当前线程进入等待状态。
1.1.1.wait执行流程
- 使当前执⾏代码的线程进⾏等待。(把线程放到等待队列中)。
- 释放当前的锁。
- 满⾜⼀定条件时被唤醒, 重新尝试获取这个锁。
1.1.2.wait结束等待的条件
- 其他线程调⽤该对象的 notify ⽅法。wait/notify 唤醒顺序是无序的。
- wait 等待时间超时 (wait ⽅法提供⼀个带有 timeout 参数的版本, 来指定等待时间)。
- 其他线程调⽤该等待线程的 interrupted ⽅法, 导致 wait 抛出 InterruptedException 异常。

/*** wait使用*/
public class WaitDemo {public static void main(String[] args) {Object lock = new Object();Thread t1 = new Thread(() -> {System.out.println("线程1开始执行");try {synchronized (lock) {System.out.println("线程1调用wait方法....");//无限期等待状态lock.wait();}} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1执行完成");},"线程1");t1.start();}
}
import java.util.concurrent.TimeUnit;public class WaitSleepDemo7 {public static void main(String[] args) throws InterruptedException {Object lock = new Object();Thread t1 = new Thread(() -> {synchronized (lock){System.out.println("线程1:开始执行");try{lock.wait(0);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1:结束执行");}System.out.println("线程1:终止执行");},"wait0");t1.start();TimeUnit.SECONDS.sleep(1);System.out.println("执行线程1的终止方法");t1.interrupt();}
}
1.1.3.wait() VS wait(long timeout)
1-不同点:wait(long timeout):当线程超过了设置的时间之后,自动恢复执行;而wait():无限等待状态。(0表示无限等待状态)
2-不同点:wait(long timeout):线程会进入WAITING状态;而wait():线程会进入TIMED_WAITING状态。
import java.time.LocalDateTime;public class WaitDemo4 {public static void main(String[] args) {Object lock = new Object();Object lock2 = new Object();new Thread(() -> {System.out.println("线程1:开始执行");synchronized (lock){try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1:执行完成");}},"无参wait线程").start();new Thread(() -> {synchronized (lock2){System.out.println("线程2:开始执行 |" + LocalDateTime.now());try {lock2.wait(60 * 60 * 60 * 1000); //1h} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程2:执行完成 |" + LocalDateTime.now());}},"有参wait线程").start();}
}
3-共同点:无论是wait(long timeout)还是wait(),都会使当前线程进入休眠状态。
4-共同点:无论是wait(long timeout)还是wait(),都能使用notify/notifyAll进行唤醒。
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;public class WaitDemo6 {public static void main(String[] args) {Object lock = new Object();new Thread(() -> {System.out.println("线程1:开始执行");synchronized (lock){try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1:执行完成");}},"无参wait线程").start();new Thread(() -> {synchronized (lock){System.out.println("线程2:开始执行 |" + LocalDateTime.now());try {lock.wait(60 * 60 * 60 * 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程2:执行完成 |" + LocalDateTime.now());}},"有参wait线程").start();new Thread(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock){System.out.println("唤醒所有线程");lock.notifyAll();}}).start();}
}
1.1.4.为什么wait要放在Object中?
wait 使用要加锁,也就是要操作锁,锁是针对对象级别的而非线程级别的,线程和对象是⼀对多,所以 wait 最便利的方式是放在 Object 中。
wait(num)和sleep(num):当num>0时,二者执行效果一样。
--->PS:wait(0) 和 sleep(0) 的区别
- wait(0) 表示无期限地等待,直到有线程唤醒它为止。
- Thread.sleep(0) 调用后会让出CPU执行权,让线程稍做休眠,然后重新调度,重新触发一次 CPU 竞争,不管是否竞争到CPU执行权,都会继续执行,直到执行结束。(类似于Thread.yield())
/*** wait(0)和sleep(0)*/ public class WaitSleepDemo7 {public static void main(String[] args) throws InterruptedException {Object lock = new Object();Thread t1 = new Thread(() -> {synchronized (lock){System.out.println("线程1:开始执行");try{lock.wait(0);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1:执行结束");}},"wait(0)");t1.start();Thread t2 = new Thread(() -> {System.out.println("线程2:开始执行");try {Thread.sleep(0);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程2:执行结束");},"sleep(0)");t2.start();} }
--->PS:wait 和 sleep 释放锁行为的区别
wait和sleep在有锁的情况下,锁的处理行为是完全不同的:
- wait方法(不管是有参还是无参)在执行时都会释放锁。
- sleep方法不会释放锁。
import java.util.concurrent.TimeUnit;/*** wait和sleep释放锁行为的区别*/ public class WaitSleepDemo8 {public static void main(String[] args) throws InterruptedException {Object lock = new Object();Object lock2 = new Object();Thread t1 = new Thread(() -> {synchronized (lock){System.out.println("线程1:开始执行");try{lock.wait(3 * 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1:结束执行");}},"wait");t1.start();Thread t2 = new Thread(() -> {synchronized (lock2) {System.out.println("线程2:开始执行");try {Thread.sleep(3 * 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程2:结束执行");}},"sleep");t2.start();//创建2个线程,先让线程休眠1秒之后,尝试获取锁,看能不能获取到锁//如果可以获取到锁,说明休眠时线程是释放锁的,而如果获取不到锁,说明是不释放锁的Thread t3 = new Thread(() -> {try{TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("尝试获取wait方法的锁");synchronized (lock){System.out.println("成功获取wait的锁");}},"wait2");t3.start();Thread t4 = new Thread(() -> {try{TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("尝试获取sleep方法的锁");synchronized (lock2){System.out.println("成功获取sleep的锁");}},"sleep2");t4.start();} }
为什么wait释放锁而sleep不释放锁?
JVM 强制语法检查,wait 方法默认等待无期限。
--->PS:(常见面试题)wait 和 sleep 的区别(小结)
二者的相同点:
- 都可以让线程休眠。
- 都可以响应 interrupt 中断的请求。
- wait(num) 和 sleep(num):当num>0时,二者执行效果一样。
二者的不同点:
- wait 必须在 synchronized 中使用;而 sleep 却不用。
- wait 是 Object 的方法(属于对象级别);而sleep 是 Thread 的方法(属于线程级别)。
- wait 释放锁;而sleep 不释放锁。
- wait 有可能无限期地等待下去;而sleep 有明确的终止等待时间。 即:wait 可传参,也可不传参;而 sleep必须要传递一个数值类型的参数,否则会报错。
- wait 和 sleep 产生的线程状态是不同的:无参的 wait 是 WAITING 状态;而sleep 是 TIMED_WAITING 状态。
- 一般情况下(响应 interrupt 除过),wait 可以接收一个 notify/notifyAll 之后就继续执行;而 sleep 只能等待超过时间之后再恢复执行。
- wait(0) 表示无期限地等待;而sleep(0)表示重新触发一次 CPU 竞争。
--->PS:3种方法让线程进入休眠/等待状态
- sleep(传参设置休眠时间;不可唤醒)
- TimeUnit(传参设置休眠时间;不可唤醒)
- wait(可传参设置休眠时间,也可不传参无限等待;可以唤醒)
1.2.notify():唤醒当前对象上一个休眠的线程(随机)。
-
⽅法notify()也要在同步⽅法或同步块中调⽤,该⽅法是⽤来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
-
如果有多个线程等待,则由线程调度器随机挑选出⼀个呈 wait 状态的线程。(并没有 "先来后到") (官方是随机的,但具体实现针对不同的JVM是不一样的)
/*** notify使用*/
public class WaitDemo2 {public static void main(String[] args) {Object lock = new Object();Object lock2 = new Object();Thread t1 = new Thread(() -> {System.out.println("线程1开始执行");try {synchronized (lock) {System.out.println("线程1调用wait方法....");//无限期的等待状态lock.wait();}} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1执行完成");},"线程1");Thread t2 = new Thread(() -> {System.out.println("线程2开始执行");try {synchronized (lock) {System.out.println("线程2调用wait方法....");//无限期的等待状态lock.wait();}} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程2执行完成");},"线程2");Thread t3 = new Thread(() -> {System.out.println("线程3开始执行");try {synchronized (lock2) {System.out.println("线程3调用wait方法....");//无限期的等待状态lock2.wait();}} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程3执行完成");},"线程3");t1.start();t2.start();t3.start();//唤醒lock对象上休眠的线程(随机唤醒一个)Thread t4 = new Thread(() -> {try {Thread.sleep(1500);} catch (InterruptedException e) {}System.out.println("线程4:开始执行,唤醒线程");synchronized (lock) {//发出唤醒通知lock.notify();System.out.println("线程4:执行了唤醒操作");try{Thread.sleep(2000);} catch (InterruptedException e) {}System.out.println("线程4:synchronized执行完了");}}, "线程4");t4.start();}
}
1.3.notifyAll():唤醒当前对象上的所有线程。
/*** notifyAll使用*/
public class WaitDemo3 {public static void main(String[] args) {Object lock = new Object();Object lock2 = new Object();Thread t1 = new Thread(() -> {System.out.println("线程1开始执行");try {synchronized (lock) {System.out.println("线程1调用wait方法....");//无限期的等待状态lock.wait();System.out.println("线程1:恢复执行之后又进入休眠状态");Thread.sleep(2000);System.out.println("线程1执行完成");}} catch (InterruptedException e) {e.printStackTrace();}},"线程1");Thread t2 = new Thread(() -> {System.out.println("线程2开始执行");try {synchronized (lock2) {System.out.println("线程2调用wait方法....");//无限期的等待状态lock2.wait();System.out.println("线程2:恢复执行之后又进入休眠状态");Thread.sleep(2000);System.out.println("线程2执行完成");}} catch (InterruptedException e) {e.printStackTrace();}},"线程2");Thread t3 = new Thread(() -> {System.out.println("线程3开始执行");try {synchronized (lock) {System.out.println("线程3调用wait方法....");//无限期的等待状态lock.wait();System.out.println("线程3:恢复执行之后又进入休眠状态");Thread.sleep(2000);System.out.println("线程3执行完成");}} catch (InterruptedException e) {e.printStackTrace();}},"线程3");t1.start();t2.start();t3.start();//唤醒lock对象上休眠的线程(随机唤醒一个)Thread t4 = new Thread(() -> {try {Thread.sleep(500);} catch (InterruptedException e) {}System.out.println("线程4:开始执行,唤醒线程");synchronized (lock){//发出唤醒通知lock.notifyAll();System.out.println("线程4:执行了唤醒操作");try{Thread.sleep(2000);} catch (InterruptedException e) {}System.out.println("线程4:synchronized执行完了");}}, "线程4");t4.start();}
}
2.注意事项:
①wait/notify/notifyAll必须要配合synchronized一起使用,否则会报错(JVM的强制规定,之所以这样规定,是为了解决线程通讯时执行混乱的问题,synchronized起到约束限制的作用)。
②wait/notify/notifyAll进行synchronized加锁,一定要使用同一个对象进行加锁。
③当调用了notify/notifyAll之后,当前线程不会⻢上释放该对象锁,要等到执⾏notify/notifyAll⽅法的线程将程序执⾏完,也就是退出同步代码块(synchronized)之后才会释放对象锁。【程序也并不会立即恢复执行,而是尝试获取锁,只有得到锁之后才能继续执行。】
④notify/notifyAll可以多次调用,也可以在wait之前调用(是无用功)。
⑤notifyAll 并不是唤醒所有 wait 等待的线程,而是唤醒当前对象处于 wait 等待的所有线程。
相关文章:

JavaWeb12-线程通讯(线程等待和唤醒)
目录 1.方法介绍 1.1.wait()/wait(long timeout):让当前线程进入等待状态。 1.1.1.wait执行流程 1.1.2.wait结束等待的条件 1.1.3.wait() VS wait(long timeout) 1.1.4.为什么wait要放在Object中? --->PS:wait(0) 和 sleep(0) 的区…...

江苏专转本如何事半功倍的备考
专转本如何事半功倍的备考 一个人学习成绩的优劣取决于他的学习能力,学习能力包括三个要素:规范的学习行为;良好的学习习惯;有效的学习方法。有了规范的学习行为才能培养出良好的学习习惯,形成了良好的学习习惯就会形成…...

linux下安装mongoDB
一、下载mongoDB包 下载地址: https://www.mongodb.com/try/download/community 个人建议:如果是学习阶段,使用5以下版本更好些。 二、安装及配置 1、安装 # 1、解压 $ tar -zxvf mongodb-linux-x86_64-rhel70-4.4.19-rc1.tgz# 2、迁移目…...

掌握MySQL分库分表(七)广播表、绑定表实战,水平分库+分表实现及之后的查询和删除操作
文章目录什么是广播表广播表实战数据库配置表Java配置实体类配置文件测试广播表水平分库分表配置文件运行测试什么是绑定表?绑定表实战配置数据库配置Java实体类配置文件运行测试水平分库分表后的查询和删除操作查询操作什么是广播表 指所有的分片数据源中都存在的…...

企业为什么需要数据可视化报表
数据可视化报表是在商业环境、市场环境已经改变之后,发展出来为当前企业提供替代解决办法的重要方案。而且信息化、数字化时代,很多企业已经进行了初步的信息化建设,沉淀了大量业务数据,这些数据作为企业的资产,是需要…...

5个有效的华为(HUAWEI)手机数据恢复方法
5个有效的手机数据恢复方法 华为智能手机中的数据丢失比许多人认为的更为普遍。发生这种类型的丢失有多种不同的原因,因此数据恢复软件的重要性。您永远不知道您的智能手机何时会在这方面垮台;因此,预防总比哀叹好,这就是为什么众…...

【Java并发编程】线程安全(一)Synchronized原理
Synchronized底层实现 简单来说,Synchronized关键字的执行主体是线程对象,加锁是通过一个锁对象来完成的是,而锁对象底层关联了一个c源码的monitor的对象,monitor对象底层又对应了操作系统级别的互斥锁,同一时刻只有一…...

[apollo]vue3.x中apollo的使用
[apollo]vue3.x中apollo的使用通过客户端获取Apollo配置环境工具的安装获取Apollo配置相关代码错误提示Uncaught (in promise) Error: Apollo client with id default not found. Use provideApolloClient() if you are outside of a component setup通过开放接口获取Apollo配置…...

system()函数启用新进程占有原进程的文件描述符表的问题
我在A程序中占用了/dev/video0这个独占模式的设备文件,在A中用system函数启用了B程序,B程序的代码中并不包含对/dev/video0的访问,但是我发现B程序也占用了/dev/video0,并且我在A程序中关闭了/dev/video0后,A程序不再占…...

nignx(安装,正反代理,安装tomcat设置反向代理,ip透传)
1安装nginx 安装wget Yum install -y wget 下载(链接从官网找到右键获取) 以下过程root 安装gcc Yum -y install gcc c 安装pcre Yum install -y pcre pcre-devel Openssl Yum install -y openssl openssl-devel 安装zlib Yum install -y zlib zlib-devel 安装make Yum inst…...

sklearn模块常用内容解析笔记
文章目录 回归模型评价指标R2_score预备知识R2_score计算公式r2_score使用方法注意事项参考文献回归模型评价指标R2_score 回归模型的性能的评价指标主要有:RMSE(平方根误差)、MAE(平均绝对误差)、MSE(平均平方误差)、R2_score。但是当量纲不同时,RMSE、MAE、MSE难以衡量模…...

我的 System Verilog 学习记录(2)
引言 从本文开始,就开始系统学习 System Verilog ,不只是语法,还有结合 Questa Sim 的实际编程练习、Debug。 本文简单介绍 System Verilog 语言的用途以及学习的必要性。 前文链接: 我的 System Verilog 学习记录(…...

【调研报告】Monorepo 和 Multirepo 的风格对比及使用示例
带有权重的Monorepo和Multirepo对比 功能/特性MonorepoMultirepoMonorepo权重值Multirepo权重值代码管理管理多个代码库更加复杂管理单个代码库更加简单37依赖管理可以简化依赖管理依赖冲突可能会更加困难73构建和部署构建和部署更加容易构建和部署可能需要更多的配置82团队协…...

Retrofit源码分析
文章目录一、简介二、源码分析2.1Retrofit的本质流程2.2源码分析2.2.1 创建Retrofit实例步骤1步骤2步骤3步骤4步骤5总结2.2.2创建网络请求接口的实例外观模式 & 代理模式1.外观模式2. 代理模式步骤3步骤4总结2.2.3执行网络请求同步请求OkHttpCall.execute()1.发送请求过程2…...

Mybatis-Plus入门系列(20) -兼容多种数据库
有道无术,术尚可求,有术无道,止于术。 文章目录前言方案分析1. 分页2. XML自定义SQL案例演示1. 配置2. 简单分页查询3. 带方言的分页查询参考前言 在我们实际开发软件产品过程中,数据库的类型可能不是确定的,也有客户…...

JetPack板块—Android X解析
Android Jetpack简述 AndroidX 是Android团队用于在Jetpack中开发,测试,打包,发布和版本管理的开源项目。相比于原来的Android Support库,AndroidX 可以称得上是一次重大的升级改进。 和Support库一样,AndroidX与Android 操作系…...

C++学习笔记-数字
当我们使用数字时,通常我们使用原始数据类型,例如 int,short,long,float 和 double 等。数字数据类型,它们的可能值和取值范围在讨论 C 数据类型时已经解释了。 C 定义数字 我们已经在之前笔记的各种实例…...

Nginx——Nginx的基础原理
摘要 Nginx 是俄罗斯人编写的十分轻量级的 HTTP 服务器,是一个高性能的HTTP和反向代理服务器,同时也是一个 IMAP/POP3/SMTP 代理服务器。Nginx 是由俄罗斯人 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,它已经在该站点运行超过两年半了。…...

服务端开发Java之备战秋招面试篇1
在这个面试造火箭工作拧螺丝的时代背景下,感觉不是很好,不过还好也是拿到了还行的offer,准备去实习了,接下来就是边实习边准备秋招了,这半年把(技术栈八股文面经算法题项目)吃透,希望…...

【C++的OpenCV】第三课-OpenCV图像加载和显示
我们开始学习OpenCV一、OpenCV加载图片和显示图片1.1 imread()函数的介绍1.2 cv::namedWindow()函数的介绍1.4 imshow()函数介绍1.5 Mat容器介绍二、 代码实例(带注释)2.1 代码2.2 执行结果一、OpenCV加载图片和显示图片 本章节中,将会学习到…...

【面试1v1实景模拟】Spring事务 一文到底
老面👴:小伙子,了解Spring的事务吗? 解读🔔:这个必须了解,不了解直接挂~😂😂😂,但面试官肯定不是想听你了解两个字,他是想让你简单的介绍下。 笑小枫🍁:了解,事务在逻辑上是一组操作,要么执行,要不都不执行。主要是针对数据库而言的,比如说 MySQL。为…...

Neuron Selectivity Transfer 原理与代码解析
paper:Like What You Like: Knowledge Distill via Neuron Selectivity Transfercode:https://github.com/megvii-research/mdistiller/blob/master/mdistiller/distillers/NST.py本文的创新点本文探索了一种新型的知识 - 神经元的选择性知识,…...

vue项目关闭子页面,并更新父页面的数据
今天下午是一个非常痛苦的,想要实现一个功能: 父页面打开了一个新的页面(浏览器打开一个新的窗口),并在子页面提交数据之后,父页面的数据要同步更新。 难点:父页面是一个表格列表,…...

第五次作业:修改redis的配置文件使得windows的图形界面客户端可以连接redis服务器
1. 安装 Redis 依赖 Redis 是基于 C语言编写的,因此首先需要安装 Redis 所需要的 gcc 依赖: yum install -y gcc tcl 2、上传安装文件 将下载好的 redis-6.2.7.tar.gz 安装包上传到虚拟机的任意目录(一般推荐上传到 /usr/local/src目录&am…...

【11】FreeRTOS的延时函数
目录1.延时函数-介绍2.相对延时函数-解析2.1函数prvAddCurrentTaskToDelayedList-解析2.3滴答定时器中断服务函数xPortSysTickHandler()-解析2.4函数taskSWITCH_DELAYED_LISTS() -解析3.延时函数-实验4.总结1.延时函数-介绍 函数描述vTaskDelay()相对延时xTaskDelayUntil()绝对…...

Vue页面组成及常用属性
一、Vue页面组成 目前的项目中,Vue页面都是采用组件套娃的形式,由一个一个的组件拼接而成整个页面。一个组件就是一个.vue文件。组件通常由template和script两部分组成: template部分:页面展示的具体元素内容,比如文字…...

j6-IO流泛型集合多线程注解反射Socket
IO流 1 JDK API的使用 2 io简介 输入流用来读取in 输出流用来写出Out 在Java中,根据处理的数据单位不同,分为字节流和字符流 继承结构 java.io包: File 字节流:针对二进制文件 InputStream --FileInputStream --BufferedInputStre…...

创业能否成功?这几个因素很重要!
创业能否成功?这几个因素很重要! 2023-02-22 19:06:53 大家好,我是你们熟悉而又陌生的好朋友梦龙,一个创业期的年轻人 上周末跟朋友一起钓鱼,他跟吐槽现在生意越来越难做。他是我身边可以说是创业很成功的例子&#…...

Bmp图片格式介绍
Bmp图片格式介绍 介绍 BMP是英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格式,能够被多种Windows应用程序所支持。随着Windows操作系统的流行与丰富的Windows应用程序的开发,BMP位图格式理所当然地被…...

Day4 leetcode
Day4 啊啊啊啊,什么玩意,第一次因为测评没过,约好的面试取消了,好尴尬呀,还有一家厦门的C/C电话面,是一家我还挺喜欢的公司,面的稀烂,只能安慰自己我现在手上至少有一个offer 有效括…...