java的wait/notify的通知机制可以用来实现线程间通信。wait表示线程的等待,调用该方法会导致线程阻塞,直至另一线程调用notify或notifyAll方法才可另其继续执行。经典的生产者、消费者模式即是使用wait/notify机制得以完成。
一、举个例子
Java线程进行通信,我们这里想到的是用wait和notify的模式,那么这里先举个例子,流程如下:
1、客人点菜,点完菜就等待
2、老板收到菜名,做菜,做好菜后通知客人吃
3、客人接收到通知,开始吃菜。3、客人接收到通知,开始吃菜。
我这里用一个很简单的例子来处理上面的逻辑,如下;
/**
* 线程之间的消息通知
* @author Java全栈suibibk.com
*
*/
publicclassWaitAndNotify{
privateObjectnotify=newObject();
publicstaticvoidmain(String[]args){
WaitAndNotifywaitAndNotify=newWaitAndNotify();
//客人开始点菜
newThread(waitAndNotify.newGuest()).start();
newThread(waitAndNotify.newBoss()).start();
}
//客人必须等老板做好饭后才能吃饭
classGuestimplementsRunnable{
@Override
publicvoidrun(){
synchronized(notify){
//开始点菜
try{
System.out.println("客人:老板,我要吃酸菜鱼。。。。");
//等待老板做,这里是无限期等待,老板没做好,我就不走
notify.wait();
}catch(InterruptedExceptione){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//老板必须等客人点餐后才能做饭
classBossimplementsRunnable{
@Override
publicvoidrun(){
synchronized(notify){
System.out.println("老板:好勒!");
System.out.println("老板:杀鱼、热油、加入配菜、出锅...");
try{
//休息十秒钟
Thread.sleep(10000);
}catch(InterruptedExceptione){
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("。。。。。。。。过了十分钟!");
System.out.println("老板:客人,您的酸菜鱼做好了,请慢用!!");
//通知客人已经做好了
notify.notify();
}
}
}
}
运行结果如下
老板:好勒!
老板:杀鱼、热油、加入配菜、出锅...
。。。。。。。。过了十分钟!
老板:客人,您的酸菜鱼做好了,请慢用!!
二、改为超时模式
只需要把wait调用改为如下即可
publicvoidrun(){
synchronized(notify){
//开始点菜
try{
System.out.println("客人:老板,我要吃酸菜鱼。。。。");
//改为一秒超时
notify.wait(1000);
System.out.println("这么久不上菜,劳资不吃了");
}catch(InterruptedExceptione){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
再次运行,却发现,明明过了一秒钟,为啥后面一句迟迟不打印?
三、原因分析
其实我们知道,在多线程通信中有两个队列,一个是同步队列,一个是等待队列。我们来大概分析一下上面的流程:
1、客人线程获取notify锁,此时老板线程进入同步队列
2、客人线程执行wait方法,此时将锁交出,自己进入等待对列
3、老板线程获得notify锁,开始做酸菜鱼,做了十秒钟
4、1秒钟后,客人线程的wait方法超时,但是没有办法,超时只是从等待对列出来,但是最多进入同步队列,必须等劳保线程执行完把notify对象的锁释放,客人线程才能够执行。
所以,就算超时了,是否能够获取到锁,还是得去竞争。也就是notify和notifyAll都不释放锁,只是唤醒正在等待这个对象的monitor的线程,但其是否能得到monitor取决于cpu调度。
一个线程被唤醒可能有一下四种情况其它的线程调用 obj.notify(),且当前线程 T,正好是被选中唤醒的。
其它的线程调用 obj.notifyAll()。
其它线程中断 T。
指定的等待时间(timeout)超时,(时间精度会有些误差)。
但是上面分析,唤醒是唤醒,能否获得到锁,还是得竞争。
四、有人就会想,那我老板线程不用notify锁不就行了
试一下,会发现报如下错误:
java.lang.IllegalMonitorStateException
为什么呢,首先我们了解下wait和notify为什么要放在synchronized里面?
wait方法的语义有两个,
释放当前的对象锁、
使得当前线程进入阻塞队列,使得当前线程进入阻塞队列,
而这些操作都和监视器是相关的,所以wait必须要获得一个监视器锁。notify也一样,它是唤醒一个线程,所以需要知道待唤醒的线程在哪里,就必须找到这个对象获取这个对象的锁然后去到这个对象的等待队列去唤醒一个线程。
所以,必须用同一个锁啦,不然唤醒不了,根本找不到要唤醒的线程。
五、等待/通知的经典范式
上面的例子没用使用while循环把wait包裹起来在多线程的情况下是有问题的,因为唤醒线程不一定获得到锁,并且就算获取的到锁也不一定能够满足条件,如果不满足条件还是需要交出去,等待通知的经典范式应该如下所示:
等待方遵循如下原则
1、获取对象的锁
2、如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件
3、条件满足则执行对应的逻辑
伪代码如下
synchronized(对象){
while(条件不满足){
对象.wait()
}
对应的处理逻辑
}
通知方遵循如下原则
1、获取对象的锁
2、改变条件
3、通知所有等待在对象上的线程。
伪代码如下
synchronized(对象){
改变条件
对象.notifyAll();
}
六、总结
由上分析,我们如果用了超时等待,就不要想着唤醒自己的线程一把自己唤醒就能够得到锁,也不要想着等待超时就能马上执行,都已经把锁交出去了,当然要重新竞争啦。一般都是用notifyAll和while循环来处理。
原网址: 访问
创建于: 2022-11-18 00:44:04
目录: default
标签: 无
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
u2u转换板 - 嘉立创EDA开源硬件平台 Spring Boot 项目的轻量级 HTTP 客户端 retrofit 框架,快来试试它!_Java精选-CSDN博客 手把手教你打造一套最牛的知识笔记管理系统! - 知乎 - 想法有重合-理论可参考 安宇雨 闲鱼 机械键盘 客制化 开贴记录 文本 linux 使用find命令查找包含某字符串的文件_beijihukk的博客-CSDN博客_find 查找字符串 ---- mac 也适用 安宇雨 打字音 记录集合 B站 bilibili 自行搭建 开坑 真正的客制化 安宇雨 黑苹果开坑 查找工具包maven pom 引用地 工具网站 Dantelis 介绍的玩轴入坑攻略 --- 关于轴的一些说法 --- 非官方 ---- 心得而已 --- 长期开坑更新 [本人问题][新开坑位]关于自动化测试的工具与平台应用 机械键盘 开团 网站记录 -- 能做一个收集的程序就好了 不过现在没时间 -- 信息大多是在群里发的 - 你要让垃圾佬 都去一个地方看难度也是很大的 精神支柱 [超级前台]sprinbboot maven superdesk-app 记录 [信息有用] [环境准备] [基本完成] [sebp/elk] 给已创建的Docker容器增加新的端口映射 - qq_30599553的博客 - CSDN博客 [正在研究] Elasticsearch, Logstash, Kibana (ELK) Docker image documentation elasticsearch centos 安装记录 及 启动手记 正式服务器 39 elasticsearch 问题合集 不断更新 6.1.1 | 6.5.1 两个版本 博客程序 - 测试 - bug记录 等等问题 laravel的启动过程解析 - lpfuture - 博客园 OAuth2 Server PHP 用 Laravel 搭建带 OAuth2 验证的 RESTful 服务 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 利用Laravel 搭建oauth2 API接口 附 Unauthenticated 解决办法 - 煮茶的博客 - SegmentFault 思否 使用 OAuth2-Server-php 搭建 OAuth2 Server - 午时的海 - 博客园 基于PHP构建OAuth 2.0 服务端 认证平台 - Endv - 博客园 Laravel 的 Artisan 命令行工具 Laravel 的文件系统和云存储功能集成 浅谈Chromium中的设计模式--终--Observer模式 浅谈Chromium中的设计模式--二--pre/post和Delegate模式 浅谈Chromium中的设计模式--一--Chromium中模块分层和进程模型 DeepMind 4 Hacking Yourself README.md update 20211011
Laravel China 简书 知乎 博客园 CSDN博客 开源中国 Go Further Ryan是菜鸟 | LNMP技术栈笔记 云栖社区-阿里云 Netflix技术博客 Techie Delight Linkedin技术博客 Dropbox技术博客 Facebook技术博客 淘宝中间件团队 美团技术博客 360技术博客 古巷博客 - 一个专注于分享的不正常博客 软件测试知识传播 - 测试窝 有赞技术团队 阮一峰 语雀 静觅丨崔庆才的个人博客 软件测试从业者综合能力提升 - isTester IBM Java 开发 使用开放 Java 生态系统开发现代应用程序 pengdai 一个强大的博主 HTML5资源教程 | 分享HTML5开发资源和开发教程 蘑菇博客 - 专注于技术分享的博客平台 个人博客-leapMie 流星007 CSDN博客 - 舍其小伙伴 稀土掘金 Go 技术论坛 | Golang / Go 语言中国知识社区
最新评论