JavaEE: wait(等待) / notify (通知)

文章目录

  • wait(等待) / notify (通知)
  • 总结


wait(等待) / notify (通知)

线程在操作系统上的调度是随机的~
那么我们想要控制线程之间执行某个逻辑的先后顺序,那该咋办呢?

可以让后执行的逻辑,使用wait, 先执行的线程,在完成某些逻辑之后,通过notify来唤醒对应的wait.
另外,通过wait notify 也是为了解决"线程饥饿"问题.

举个例子:
有一个ATM机,滑稽1,进入ATM机,锁上房门后,结果发现ATM里面没钱了~
没钱了咋办,那就出来呗,于是这个老哥,就开锁出来了,但是这个老哥前脚刚迈出这个门半步,心里想:“我要不再进去看看”,于是这个老哥又把门锁上就又进去了,结果还是没钱,就出来了,走到门口,就又进去了.
就这样进进出出.他后面的滑稽也进不去~
在这里插入图片描述
把这些滑稽想象成一个个线程.第一个线程进进出出,后面的线程进不去,这就是"线程饥饿".

更准确的描述: 线程饥饿(Thread Starvation)是指在多线程程序中,某个线程无法获得所需的资源或执行所需的操作,导致其长时间等待或无法正常工作的情况。

针对上述问题,我们可以使用 wait / notify 来解决

让1号滑稽,拿到锁的时候进行判断
判断当前是否能执行"取钱"操作,如果能执行,就正常执行.
如果不能执行,那就主动释放锁,并且"阻塞等待"(通过调用wait),此时这个线程就不会在后续参与锁的竞争了.

一直阻塞到,"取钱"的条件具备了.
此时,再由其他线程通过机制(notify) 唤醒这个线程.

接着刚才的例子:

滑稽1,进入ATM之后,发现没钱,就要阻塞等待(wait)~
阻塞等待,一定是先释放锁,再等待,如果他抱着锁等待,别人就不能使用ATM机了.
在这里插入图片描述

wait 既然要释放锁,那么前提就是必须先加上锁.

    public static void main(String[] args) throws InterruptedException {Object obj = new Object();System.out.println("wait 之前");// 使用 wait 必须先加上锁!!synchronized(obj) {obj.wait();}System.out.println("wait 之后");}

如果不先加锁,就会报异常:
在这里插入图片描述
加锁后:
在这里插入图片描述
由于代码中没有notify,所以wait将一直等待下去~

wait要进行解锁,也要进行阻塞等待.(阻塞就是为了收到通知)
解锁和进行阻塞等待是同时执行的(打包成原子的,wait内部已经做好了)

如果不是原子的,不是同时执行,会咋样呢?

在这里插入图片描述
假设wait是分成两步:

  1. 释放锁
  2. 执行等待

那么这两者之间,就可能发生线程切换.
比如,此处运钱的线程穿插进来了,执行了放钱操作,并且会通知所有的等待他的线程来取钱,但是由于1号滑稽还没有执行等待操作,所以上述运钱的通知不会被1号滑稽感知到~
此时,当1号滑稽执行等待的时候,由于错过了通知,那么他将持续等待下去,无法被及时唤醒了.

wait 使调用的线程进入阻塞.
notify 则是通知wait的线程被唤醒.被唤醒的wait,就会重新竞争锁,并且在拿到锁之后,再继续执行.
在使用 wait 或 notify 时,必须要确保先加锁,才能执行.

    private static Object locker = new Object();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{synchronized(locker) {System.out.println("t1 wait 之前");try {// 使用 wait 必须先加上锁!!locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t1 wait 之后");}});Thread t2 = new Thread(()->{synchronized(locker) {System.out.println("t2 notify 之前");Scanner scanner = new Scanner(System.in);scanner.next(); // 这里主要是通过这个next来构造"阻塞"// 使用 notify 必须先加上锁!!locker.notify();System.out.println("t2 notify 之后");}});t1.start();t2.start();}

在这里插入图片描述
wait默认是死等, () 内不写东西就是默认.
wait还提供带参数的版本,来指定超时时间,如果wait达到了最大的超时时间,还没有notify,那么就不会继续等待了,而是直接继续执行.

这样看来,wait和sleep看起来就有点相似了~

但是,wait和sleep是有本质区别的:

  • 使用wait的目的是为了提前唤醒,而sleep就是固定时间的阻塞,不涉及到唤醒.虽然sleep可以被Interrupt唤醒,但是Interrupt操作表示的意思不是"唤醒",而是要终止线程了.
  • wait必须要搭配synchronized使用,并且wait会先释放锁,同时进行等待
  • sleep和锁无关,如果不加锁,sleep也能正常使用,如果加了锁,不会释放锁,而是"抱着锁"一起睡.其他线程是无法拿到锁的~

notify唤醒wait的线程,假如有多个线程都在同一个对象wait上,此时notify是如何唤醒的呢?
答: 随机唤醒其中的一个线程~

和notify相对的,还有一个操作, notifyAll 唤醒所有等待的线程.不过很少使用就是了.
因为一个一个唤醒(多执行几次notify)整个程序的执行过程是比较有序的,如果一下唤醒所有,这些被唤醒的线程就会无序的竞争锁.

总结

使用 wait(等待) / notify (通知) 可以控制线程之间执行某个逻辑的先后顺序,也可以解决"线程饥饿"问题.

线程饥饿(Thread Starvation)是指在多线程程序中,某个线程无法获得所需的资源或执行所需的操作,导致其长时间等待或无法正常工作的情况。

wait 一共做了三件事:

  1. 释放锁.
  2. 进入阻塞等待,准备接收通知.
  3. 收到通知以后,唤醒,并且重新尝试获取锁.

wait / notify 都是Object提供的方法.
wait / notify 都需要搭配synchronized使用,即先加锁,再使用.
wait / notify 要想通知生效,还需要确保是同一个对象.

wait提供了无参数版本,也提供了带参数版本(超时时间)
如果有多个线程都在wait, notify会随机唤醒其中的一个线程.
notifyAll 能够唤醒所有.

notify 调用次数超过wait的次数(没有人等待,也notify了),没有副作用~

本文到这里就结束啦~

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

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

相关文章

C++-类与对象基础

一,类的定义 1.1类定义格式 class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。类体中内容称为mian类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者…

【nginx】centos7安装并配置开机自启

【nginx】配置开机自启 1.nginx配置开机自启 安装完成nginx之后 vim /lib/systemd/system/nginx.service[Unit] Descriptionnginx Afternetwork.target[Service] Typeforking ExecStart/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf ExecReload/usr/loc…

理解Spring框架2:容器IOC

理解Spring框架2:容器IOC (qq.com)

ViP-LLaVA: Making Large Multimodal Models Understand Arbitrary Visual Prompts

发表时间:cvpr2024 论文链接:https://readpaper.com/pdf-annotate/note?pdfId2357936887983293952&noteId2426262228488986112 作者单位:University of Wisconsin–Madison Motivation:现在的多模态模型都关注整张图像的理…

torch量化接口深度解读-eager模式-fx模式

一、定义 接口总结量化模式解读 二、实现 接口总结 1. PyTorch提供了三种不同的量化模式:Eager模式量化、FX图模式量化(维护)和PyTorch 2导出量化。 2. Eager Mode Quantization是一个测试版功能。用户需要进行融合,并手动指定量…

尚硅谷谷粒商城项目笔记——六、使用navciat连接docker中的mysql容器【电脑CPU:AMD】

六、使用navciat连接docker中的mysql容器 注意: 因为电脑是AMD芯片,自己知识储备不够,无法保证和课程中用到的环境一样,所以环境都是自己根据适应硬件软件环境重新配置的,这里的虚拟机使用的是VMware。 1navicat免费…

最新版Ableton Live 12.20 WIN MAC,长期更新持续有效

一。Ableton Live 12.20 WIN &MAC 2024.08.06发布 Ableton Live Suite是一款由ABLETON公司开发的功能强大且全面的音乐制作、内容编辑和演奏分析软件。它极大地改进了许多社会功能,使音乐创作、背景音乐的开发变得更加快捷方便。 软件的主要功能包括录音、作曲…

WordPress原创插件:Category-id-list分类ID显示查看

WordPress原创插件:Category-id-list分类ID显示查看 插件设置位置在工具栏

学习vue3 五,传送,缓存组件以及过渡和过渡列表

目录 Teleport传送组件 keep-alive缓存组件 transition动画组件 1. 过渡的类名 2. 自定义过渡class名 3. transition的生命周期 4.appear transition-group 1. 过渡列表 2. 列表的移动过渡 3. 状态过渡 Teleport传送组件 Teleport Vue 3.0新特性之一。 Teleport 是一…

Spring的配置类分为Full和Lite两种模式

Spring的配置类分为Full和Lite两种模式 首先查看 Configuration 注解的源码, 如下所示: Target({ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Documented Component public interface Configuration {AliasFor(annotation Component.class)String value() defau…

(C23/C++23) 语句末尾的标签

文章目录 🔖前言🏷️ref🏷️标号 🔖兼容🏷️23标准前🏷️23标准后🏷️原因 🔖未兼容🔖END🌟关注我 🔖前言 🏷️ref C23提案复合语句末…

Serverless 1

一、云原生应用 云原生应用覆盖到: 大数据,人工智能,边缘计算,区块链等 服务代理:envoy API 网关:APISIX 服务网格:Istio 服务发现:CoreDNS 消息和流式处理:kafka Serve…

PDF预览:利用vue3-pdf-app实现前端PDF在线展示

目录 PDF预览:利用vue3-pdf-app实现前端PDF在线展示 一、vue3-pdf-app组件介绍及其优点 1、vue3-pdf-app是什么 2、作用与场景 3、类似的插件 二、项目初始化与依赖安装 1、初始化Vue3项目 2、安装依赖 三、集成vue3-pdf-app插件 1、引入插件 2、配置组件…

ChemLLM化学大模型再升级,AI助力化学研究

ChemLLM 介绍 ChemLLM 系列模型 是由上海人工智能实验室基于InternLM2 开发的首个兼备化学专业能力和对话、推理等通用能力的开源大模型。相比于现有的其他大模型,ChemLLM 对化学空间进行了有效建模,在分子、反应和其他领域相关的化学任务上表现优异。 …

解决戴尔台式电脑休眠后无法唤醒问题

近期发现有少量戴尔的台式机会有休眠后无法唤醒的问题,具体现象就是电脑在休眠后,电源指示灯以呼吸的频率闪烁,无论怎么点鼠标和键盘都没有反应,并且按开机按钮也没法唤醒,只能是长按开机键强制关机再重启才行&#xf…

leetcode 958.二叉树的完全性检验

1.题目要求: 给你一棵二叉树的根节点 root ,请你判断这棵树是否是一棵 完全二叉树 。在一棵 完全二叉树 中,除了最后一层外,所有层都被完全填满,并且最后一层中的所有节点都尽可能靠左。最后一层(第 h 层)…

YOLOv8改进 | 主干网络 | 将backbone替换为MobileNetV4【小白必备教程+附完整代码】

秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡 专栏目录 :《YOLOv8改进有效…

Linux PCI和PCIe总线

1 PCIe中断 - PCI/PCIe设备中断都是level触发,并且请求信号为低电平有效 - PCI总线一般只有INTA#到INTD#的4个中断引脚,所以PCI多功能设备的func一般不会超过4个,但是共享中断除外 2 IOMMU 2.1 ARM SMMU v2 Refer to my blog ARM SMMU v2. 2.…

【机器学习】重塑游戏世界:机器学习如何赋能游戏创新与体验升级

📝个人主页🌹:Eternity._ 🌹🌹期待您的关注 🌹🌹 ❀目录 🔍1. 引言:游戏世界的变革前夜📒2. 机器学习驱动的游戏创新🌞智能化游戏设计与开发&…

OJ-0807

题目 参考 import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);String input in.nextLine();String[] numStrs inp…