wait() 、notify()、notifyAll() 的详细用法

文章目录

  • 💐wait() 讲解
  • 💐notify() 讲解
  • 💐notifyAll()
  • 💡wait() 和 sleep() 的区别

首先,我们知道,线程的执行顺序是随机的(操作系统随机调度的,抢占式执行),但是有时候,我们并不喜欢这种随机的执行,更喜欢的是它们能够顺序的执行,所以,Java就引入了一种机制,wait() 和 notify() ,它们的 作用就是保证线程执行的顺序;

而前面的文章中也讲过一个方法 join(),也是能影响线程的执行顺序,但是呢,这个join只能控制线程结束的顺序,而我们想要的是,线程不结束,也能按照我们自己规定的顺序去依次执行;

💐wait() 讲解

使用 wait() 时要注意一定要搭配 synchronized 使用,否则的话就会抛出异常

在这里插入图片描述

调用 wait() 时,wait() 会做三件事:

  • 使当前的线程处于阻塞等待
  • 释放当前线程获取到的锁
  • 在其他线程中使用锁对象调用notify时或者使用带参数的wait(带有时间参数,超过时间就会被唤醒)被唤醒后,会再重新尝试获取锁
public class Main {public static void main(String[] args) {Object locker = new Object();Thread thread1 = new Thread(() -> {synchronized (locker) {System.out.println("调用wait(), 阻塞等待");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("thread1被唤醒");}});thread1.start();}
}

在这里插入图片描述

可以看到,当执行到 wait() 这行代码时,就一直处于了阻塞等待,需要在其他线程中使用 notify() 来唤醒,而目前代码中没有其他线程,所以就一直等待;这里也需要解释一下,关于调用 wait() 方法,因为 wait() 方法是Object类中的方法,所以,所有的对象都可以调用;

💐notify() 讲解

notify 作用:唤 醒其它调用了 wait() 导致阻塞等待的线程;

1. wati() 和 notify() 都要在加锁的代码块中使用,并且由锁对象调用

2. 使用 notify() 唤醒某个线程时,只能唤醒和调用 notify() 是同一个锁的线程

比如:thread1 线程中使用 锁对象 locker1 调用了 wait() 方法阻塞等待,那么在其他线程中,也要使用 locker1 来调用 notify() 方法对 thread1 进行唤醒

 public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread thread1 = new Thread(() -> {synchronized (locker) {System.out.println("thread1执行,调用wait,进行阻塞,同时锁被释放");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("thread1执行完");}});Thread thread2 = new Thread(() -> {//先进行一个睡眠,可以明显的观察效果try {Thread.sleep(100);//让thread1先执行System.out.println("thread2执行,进入睡眠四秒");Thread.sleep(4000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker) {System.out.println("四秒后");System.out.println("进行唤醒");locker.notify();}});thread1.start();thread2.start();}

在这里插入图片描述

wait() 、notify() 也可以避免“线程饿死”

举个例子:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

💐notifyAll()

如果多个线程都调用 wait() 的话,多个线程都会进入阻塞,调用 notify() 的话,就只能唤醒一个,但是用 notifyAll() 的话,就可以一次性全部唤醒(唤醒的是等待 同一个锁的所有线程),这里要注意一点:多个线程被同时唤醒时,只有一个线程可以成功的获取到锁,其他的线程进行继续等待

场景:有四个线程,thread1,thread2,thread3 调用 wait() 进行阻塞等待,thread4 调用 notify(),最终,三个阻塞的线程只会有一个被唤醒,代码如下:

public class Main {public static void main(String[] args) {Object locker = new Object();Thread thread1 = new Thread(() -> {synchronized (locker) {System.out.println("thread1调用wait(), 阻塞等待");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("thread1被唤醒");}});Thread thread2 = new Thread(() -> {synchronized (locker) {System.out.println("thread2调用wait(), 阻塞等待");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("thread2被唤醒");}});Thread thread3 = new Thread(() -> {synchronized (locker) {System.out.println("thread3调用wait(), 阻塞等待");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("thread3被唤醒");}});Thread thread4 = new Thread(() -> {try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker) {locker.notify();System.out.println("thread4调用notify()");}});thread1.start();thread2.start();thread3.start();thread4.start();}
}

执行结果:

在这里插入图片描述

如果将 notify() 换成 调用 notifyAll(),那么就会全部被唤醒,代码如下:

        Thread thread4 = new Thread(() -> {try {Thread.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker) {locker.notifyAll();System.out.println("thread4调用notifyAll()");}}

通过结果可以看到,当 thread4 调用了 notifyAll() 后, thread3 先获取到了锁,然后释放锁后,thread2 又获取到了锁,最后是 thread1

在这里插入图片描述

💡wait() 和 sleep() 的区别

  • 类不同:sleep() 是Thread线程类的静态方法, wait() 是 Object类的方法
  • 调用后是否释放锁: sleep() 调用后不会有释放锁的操作; wait() 调用后会释放锁
  • 用途不同: wait() 通常用于线程间交互/通信, sleep() 通常用于暂停执行
  • 用法不同:wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用 notify() 方法, 或者 notifyAll() 方法 或者是使用wait(long timeout),指定一个阻塞时间,超时后线程自动苏醒。sleep() 方法执行完后,线程会自动苏醒。

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

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

相关文章

【MySQL】视图、索引

目录 视图视图的用途优点视图的缺点创建视图查看视图修改视图删除视图注意事项 索引索引的原理索引的数据结构二分查找法Hash结构Hash冲突!!! B树二叉查找树 存在问题改造二叉树——B树降低树的高度 B树特点案例继续优化的方向 改造B树——B树…

ip https证书360元买一年送一月

随着互联网的发展,不论是用户还是开发者,都越来越重视互联网环境的安全性。IP https证书是一种网络安全协议,用于保护网络通信的安全性和机密性。IP https数字证书是CA认证机构为只有公网IP地址,没有域名的站点颁发的数字证书&…

构建信息蓝图:概念模型与E-R图的技术解析

✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua,在这里我会分享我的知识和经验。&#x…

【JavaEE进阶】CSS选择器的常见用法

CSS选择器的主要功能就是选中页面指定的标签元素&#xff0c;选中了元素&#xff0c;才可以设置元素的属性。 CSS选择器主要有以下几种: 标签选择器类选择器id选择器复合选择器通配符选择器 接下来用代码来学习这几个选择器的使用。 <!DOCTYPE html> <html lang&q…

【Algorithms 4】算法(第4版)学习笔记 15 - 4.1 无向图

文章目录 前言参考目录学习笔记1&#xff1a;图表介绍1.1&#xff1a;定义1.2&#xff1a;常见应用1.3&#xff1a;术语1.4&#xff1a;一些图表处理问题2&#xff1a;图表 API2.1&#xff1a;图的表示2.2&#xff1a;无向图 API2.3&#xff1a;典型图处理代码2.4&#xff1a;图…

基于Apifox实现javaweb的数据响应与请求

前言 之前文章已经写过了怎么基于springboat以及maven创建javaweb项目&#xff0c;这里就不在讲述了 可以看看我之前的文章&#xff0c;前一篇发布的javaweb的数据请求与响应&#xff0c;下面具体介绍怎么基于 Apifox实现javaweb的数据响应与请求&#xff0c;顺便给大家介绍…

类与对象(二)--类的六个默认成员函数超详细讲解

目录 1.类的默认六个成员函数✒️ 2.构造函数 2.1构造函数的概念✒️ 2.2构造函数的特性✒️ 3.析构函数 3.1析构函数的概念✒️ 3.2析构函数的特征✒️ 4.拷贝构造函数 4.1拷贝构造函数的概念✒️ 4.2拷贝构造函数的特征✒️ 4.3思考❓ 4.4深拷贝和浅拷贝⭐️…

UE5 C++ TPS开发 学习记录(九

p20 首先我们现在有一个多人游戏的系统类MultiplayerSessionsSubsystem 在这个系统内提供了很多会话系统的接口SessionInterface 当现在我们有一些SessionInterfaceDelegates的委托,这个委托的来源是SessionInterface,所以我们使用的委托可以接收到来自SessionInterface的消息(…

网络学习:MPLS标签与标签分配协议—LDP

目录 前言&#xff1a; 一、MPLS标签 1、定义&#xff1a; 2、标签结构&#xff1a; 3、标签识别&#xff1a; 二、标签分配协议---LDP&#xff08;Lable Distribution Protocol&#xff09; 1、定义&#xff1a; 2、标签分配协议的种类&#xff1a; 3、LDP消息类型 …

回溯五题【Leetcode17数独/37组合问题/51N皇后/212字典树/980状态压缩】

文章目录 关于回溯37. 解数独&#xff08;37.sudoku-solver&#xff09;17. 电话号码的数字组合&#xff08;17.letter-combinations-of-a-phone-number&#xff09;51. N皇后&#xff08;51.n-queens&#xff09;212. 单词搜索 II&#xff08;212.word-search-ii&#xff09;简…

K次取反后最大化的数组和 加油站 分发糖果 柠檬水找零

1005.K次取反后最大化的数组和 力扣题目链接(opens new window) 给定一个整数数组 A&#xff0c;我们只能用以下方法修改该数组&#xff1a;我们选择某个索引 i 并将 A[i] 替换为 -A[i]&#xff0c;然后总共重复这个过程 K 次。&#xff08;我们可以多次选择同一个索引 i。&a…

安装算法依赖时版本报错,依赖之间对应版本

困惑了很久&#xff0c;毕竟不是计算机专业专业出身&#xff0c;才知道安装深度学习算法各个依赖之间是有版本对应关系的。 &#xff08;本文使我随笔记录&#xff0c;无价值&#xff09; 比如&#xff1a; 再比如&#xff1a; 由于我第一步安装cuda时就和其他博主不一致&…

Vue基础入门(2)- Vue的生命周期、Vue的工程化开发和脚手架、Vue项目目录介绍和运行流程

Vue基础入门&#xff08;2&#xff09;- Vue的生命周期、Vue的工程化开发和脚手架、Vue项目目录介绍和运行流程 文章目录 Vue基础入门&#xff08;2&#xff09;- Vue的生命周期、Vue的工程化开发和脚手架、Vue项目目录介绍和运行流程5 生命周期5.1 Vue生命周期钩子5.2 在creat…

docker pull 拉取失败,设置docker国内镜像

遇到的问题 最近在拉取nginx时&#xff0c;显示如下错误&#xff1a;Error response from daemon: Get “https://registry-1.docker.io/v2/”: net/http: request canceled (Client.Timeout exceeded while awaiting headers)。 这个的问题是拉取镜像超时&#xff0c;通过检索…

c语言经典测试题11

1.题1 #include <stdio.h> int main() { int a[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, *p a 5, *q NULL; *q *(p5); printf("%d %d\n", *p, *q); return 0; }上述代码的运行结果是什么呢&#xff1f; 我们来分析一下&#xff1a;我们创建了一个数…

第1题:两数之和

题目内容&#xff1a; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。…

数据持久层框架:MyBatis-Plus

数据持久层框架&#xff1a;MyBatis-Plus 前言注解代码生成器CURD接口Service CRUD 接口Mapper CRUD 接口 条件构造器QueryWrapper和UpdateWrapperallEqeq、negt、ge、lt、lebetween、notBetweenlike、notLike、likeLeft、likeRight、notLikeLeft、notLikeRightisNull、isNotNu…

C 判断

判断结构要求程序员指定一个或多个要评估或测试的条件&#xff0c;以及条件为真时要执行的语句&#xff08;必需的&#xff09;和条件为假时要执行的语句&#xff08;可选的&#xff09;。 C 语言把任何非零和非空的值假定为 true&#xff0c;把零或 null 假定为 false。 下面…

UOS 20 安装redis 7.0.11 安装redis 7.0.11时 make命令 报错 /bin/sh: cc: command not found

UOS 20 安装redis 7.0.11 1、下载redis 7.0.112、安装redis 7.0.113、启动停止redis 7.0.114、安装过程问题记录 UOS 20 安装redis 7.0.11 安装redis 7.0.11时 make命令 报错 /bin/sh: cc: command not found、zmalloc.h:50:31: fatal error: jemalloc/jemalloc.h: No such fil…

代码随想录训练营第37天 | LeetCode 738.单调递增的数字、LeetCode 968.监控二叉树、

目录 LeetCode 738.单调递增的数字 文章讲解&#xff1a;代码随想录(programmercarl.com) 视频讲解&#xff1a;贪心算法&#xff0c;思路不难想&#xff0c;但代码不好写&#xff01;LeetCode:738.单调自增的数字_哔哩哔哩_bilibili 思路 ​​​​​​LeetCode 968.监控二…