多线程编程7——wait和notify、notifyAll

线程最大的问题就是抢占式执行,随机调度。可以通过一些API让线程主动阻塞,主动放弃CPU,从而控制线程之间的执行顺序。比如:join,sleep,wait和notify、notifyAll

前面章节已经介绍过 join 和 sleep了,那么接下来我们来介绍介绍wait 和 notify、notifyAll

wait 和 notify、notifyAll 是 Object 类中的方法。Java 里任意一个对象都有这两个方法。

1、wait()

正确使用 wait 的代码如下(搭配 synchronized 使用):

public class ThreadDemo15 {public static void main(String[] args) {Object object = new Object();//这个线程负责等待Thread t1 = new Thread(()->{System.out.println("wait 之前");try {synchronized (object){object.wait();}} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("wait 之后");});t1.start();}
}

在线程中调用wait,线程进入阻塞等待状态,比如在 线程t1 中调用 wait,就是让线程t1 进入阻塞等待状态(WAITING)。有有参和无参两个版本。无参版本,就是死等,直到被通知唤醒。有参版本,就是规定了一个最大的等待时间,超过时间就不等了。

wait 操作有3步:1、先释放2、进行阻塞等待 3、收到通知(notify)之后,重新尝试获取锁,并在获取锁后,继续往下执行

所以,wait 要搭配 synchronizwd 来使用,要先获取到锁,才能执行 wait 释放锁,否则就会报异常(IllegalMonitorStateException,非法的锁状态异常)。就像,你还没有女朋友,是个单身狗呢,就想分手,你要和谁分啊。

无论在线程中是通过哪个锁对象 wait 的,这个线程都会处于阻塞等待状态,且阻塞在 synchronized 代码块内,此时是释放了锁的,其他线程是可以对 锁对象 加锁的。

wait 结束等待的条件如下:

1、如果 wait 有参数,超过时间就不等了

2、interrupt 触发异常唤醒 wait

3、其他线程调用了该对象的notify方法(这个对象就是锁对象),通知在 同一个对象上 等待的线程,此时在同一个对象上等待的线程就会重新尝试加锁。(只能有一个线程会加锁成功)

报 非法的锁状态异常 的代码如下:

public class ThreadDemo15 {public static void main(String[] args) {Object object = new Object();//这个线程负责等待Thread t1 = new Thread(()->{System.out.println("wait 之前");try {object.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("wait 之后");});t1.start();}
}

没有加锁,就想执行 wait 释放锁,报异常。

2、notify()

通知在同一个对象上等待的线程也就是说,此处 notify 的对象要和 wait 的对象要相同,如果通知和等待是两个不同对象,通知就不会生效,wait就不会唤醒。

也要结合 synchronized关键字,要先获取到锁,才能通知。

public class ThreadDemo15 {public static void main(String[] args) throws InterruptedException {Object object = new Object();//这个线程负责等待Thread t1 = new Thread(()->{System.out.println("wait 之前");try {synchronized (object){object.wait();}} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("wait 之后");});//这个线程负责通知Thread t2 = new Thread(()->{System.out.println("notify之前");synchronized (object){object.notify();}System.out.println("notify之后");});t1.start();Thread.sleep(500);t2.start();}
}

四个地方的对象都要相同,比如上面,都是object。wait使用的对象要和notify使用的对象相同,不然就通知了个寂寞,notify不会有任何效果。notify 只能唤醒在同一个对象上等待的线程。

3、总结

1、wait和notify都要先获取到锁才能使用。获取到锁后,使用wait,线程会先释放锁对象,然后阻塞等待;使用notify,会通知在同一个锁对象上等待的线程,如果当前有多个线程在等待同一个锁对象,会随机唤醒一个等待的线程;而notifyAll,是所有线程都唤醒,这些线程再一起竞争锁。

2、wait和sleep的区别:

(1)虽然wait和sleep都能被提前唤醒,wait是使用notify唤醒,sleep是使用interrupt唤醒。但是notify唤醒wait,不会有任何异常,是正常的业务逻辑。而interrupt唤醒sleep,则是出现了异常,表示一个出问题的逻辑。

(2)wait需要搭配synchronized使用,sleep不需要

(3)wait是Object的方法,sleep是Thread的方法

3、wait和join的区别:

wait可以使用notify提前唤醒,但是join则必须得等这个线程彻底执行完,下个线程才能执行,不能提前唤醒。

4、wait和notify的使用 

wait和notify可以对多个线程的执行顺序进行控制。wait会让调用的线程进行阻塞,通过其他线程的notify进行通知。

如:三个线程,分别只能打印A,B,C,请你写个代码,保证这三个线程,固定按照ABC这样的顺序来打印

 public static void main(String[] args) throws InterruptedException {//让 C 后于 B 打印,让 B后于 A打印Object object1 = new Object();Object object2 = new Object();Thread t1 = new Thread(()->{System.out.println("A");synchronized (object2) {object2.notify();}});Thread t2 = new Thread(()->{synchronized (object2){try {object2.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("B");synchronized (object1){object1.notify();}});Thread t3 = new Thread(()->{synchronized (object1){try {object1.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("C");});t2.start();t3.start();//防止notify通知了个寂寞,wait没人唤醒了Thread.sleep(1000);t1.start();}

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

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

相关文章

计算机网络-IPv6地址规范与分类

昨天学习了IPv6的基础概念,了解了IPv6的由来以及地址格式,今天继续学习下IPv6的地址分类与表示。 一、IPv6地址缩写规范 IPv6地址的长度为128 bit。一般用冒号分割为8段,每一段16 bit,每一段内用十六进制表示。 IPv6地址格式 那12…

3-成功初始化 Kubernetes 控制平面后如何操作

成功初始化 Kubernetes 控制平面后的一系列指示和建议,用于帮助你开始使用你的 Kubernetes 集群。下面是详细的解释和步骤: kubeadm init --apiserver-advertise-address 172.19.35.202 --image-repository registry.cn-hangzhou.aliyuncs.com/google_c…

MATLAB 基础使用教程

MATLAB 的基本使用主要包括如下几个方面:熟悉 MATLAB 环境,数据的输入输出,基本的数学运算,图形绘制,编程等。以下通过一些例子进行简要说明。 1. 熟悉 MATLAB 环境 启动 MATLAB 后,你会看到 MATLAB 的桌面…

【kettle004】kettle访问本地MySQL数据库并处理数据至execl文件

一直以来想写下基于kettle的系列文章,作为较火的数据ETL工具,也是日常项目开发中常用的一款工具,最近刚好挤时间梳理、总结下这块儿的知识体系。 熟悉、梳理、总结下MySQL关系数据库相关知识体系 kettle访问MySQL数据库并处理数据至execl文件…

Linux 权限提升 - 信息收集 清单

这个清单主要使用于内网渗透的其中一个环节,信息收集此环节涉及后续是否有高效的攻击成绩,通过某些手段和技巧,可获取更多的彩蛋!🎆 攻击者可以发现目标系统的弱点和漏洞,包括但不限于: 发现潜…

代码随想录算法训练营Day25 | 216.组合总和III、17.电话号码的字母组合 | Python | 个人记录向

本文目录 216.组合总和III做题看文章 17.电话号码的字母组合做题看文章 以往忽略的知识点小结个人体会 216.组合总和III 代码随想录:216.组合总和III Leetcode:216.组合总和III 做题 参照着Day24中77.组合的结构,调试后AC了,代…

详解SPI、I2C、UART、I2S、GPIO、SDIO、CAN

总线,总线,总要陷进里面。这世界上的信号都一样,但是总线却成千上万,让人头疼。 总的来说,总线有三种:内部总线、系统总线和外部总线。内部总线是微机内部各外围芯片与处理器之间的总线,用于芯片一级的互连;而系统总线是微机中各插件板与系统板之间的总线,用于插件板一…

Android 生成二维码

一、生成二维码工具类封装 1、二维码库 // 二维码implementation com.journeyapps:zxing-android-embedded:4.3.0 2、工具类 /*** 二维码* 处理工具*/public class QRCodeDealUtils {/*** param content 字符串内容* param size 位图宽&高(单位:px)* param log…

接口的构成

目录 接口 一、URL 二、请求方法 三、请求报文(request message) 3.1请求行 3.2请求头 3.3 请求体 四、响应报文(response message) 4.1响应行 4.2响应头 4.3响应体 接口 接口就是API,是程序开发的函数和方…

为什么要进行人脸识别?

人脸识别技术被广泛应用于各种场景和行业,其主要目的包括但不限于以下几点: 1. **安全验证**:人脸识别可以用作身份验证的一种方式,确保只有授权人员才能进入特定的区域或访问敏感信息。例如,用于解锁手机或电脑、进入…

Linux---为什么会有粘滞位?

在前面已经讲过目录的rwx权限: 可读权限(r): 如果目录没有可读权限, 则无法用ls等命令查看目录中的文件内容. 有可写权限(w):如果目录没有可写权限,则无法在目录中创建文件, 也无法在目录中删除文件.可执行权限(x): 如果目录没有可执行权限, 则无法cd到…

MOUNT windows到本机

mount -t cifs -o username"Administrator",password"123456789",vers2.0,rw,uid0,gid0 //192.168.10.68/home /home/windows/windowsHome 其中: username"Administrator" 为Windows用户的用户名 password"123456789" …

D-Wave 推出快速退火功能,扩大量子计算性能增益

内容来源:量子前哨(ID:Qforepost) 文丨浪味仙 排版丨沛贤 深度好文:1400字丨6分钟阅读 摘要:量子计算公司 D-Wave 宣布在其 Leap™ 实时量子云服务中的所有量子处理单元 (QPU) 上推出新的快速退火功能。…

Java 网络编程之TCP(五):分析服务端注册OP_WRITE写数据的各种场景(二)

接上文 二、注册OP_WRITE写数据 服务端代码: import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.S…

C++ //练习 13.58 编写新版本的Foo类,其sorted函数中有打印语句,测试这个类,来验证你对前两题的答案是否正确。

C Primer(第5版) 练习 13.58 练习 13.58 编写新版本的Foo类,其sorted函数中有打印语句,测试这个类,来验证你对前两题的答案是否正确。 环境:Linux Ubuntu(云服务器) 工具&#xf…

RFC 6071: IP Security (IPsec) and Internet Key Exchange (IKE) Document Roadmap

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/96882d1fb67b4383bc77c4dd421f7b

【一刷《剑指Offer》】面试题 10:二进制中 1 的个数

力扣对应题目链接:191. 位1的个数 - 力扣(LeetCode) 牛客对应题目链接:二进制中1的个数_牛客题霸_牛客网 一、《剑指Offer》内容 核心考点 :二进制计算。 二、分析问题 1、循环检查二进制位 可以直接循环检查给定数字…

Qt 顺序容器类与关联容器类

hello!欢迎大家来到我的Qt学习系列之顺序容器类与关联容器类。希望这篇文章能对你有所帮助!!!大家也可以关注我的专栏,我会持续更新一些Qt的学习知识,和数据库的学习内容!!&#xff…

Springboot实现国际化以及部署Linux不生效问题

1、在application.properties 添加以下配置: #国际化配置 spring.messages.basenamei18n/messages/messages spring.messages.fallback-to-system-localefalse 2、添加配置文件在 resources目录下 如下图所示: 这个国际化文件命名有个坑,必须…

胸部X光图像分类:因果视角

Chest X-ray Image Classification: A Causal Perspective 摘要 这篇论文提出了一种新的方法来处理胸部X射线(CXR)分类问题,将因果推理融入到基于深度学习的方法中。尽管近年来深度学习在CXR分类方面取得了许多进展,但确保这些算…