wait notify notifyAll await signal signalAll 的理解及示例
从常见的一道面试题开始,题目的描述是这样子的:
有三个线程分别打印A、B、C,请用多线程编程实现,在屏幕上循环打印10次ABCABC…
网上大都教了你怎么去实现,其实我也写过一篇 https://blog.csdn.net/sanri1993/article/details/89644493 但是都没有把原理说透,说再多的解法别人也记不住。
这个其实需要从最原本的 Object 的方法 wait() ,notify() notifyAll() 来理解 ,想必读者在工作中应该几乎是没有使用过这几个方法的,这里我稍微介绍下功能
- 这几个方法都必须在线程获得锁之后才能调用(这里说的锁是
synchronized
锁) - wait 方法会阻塞当前线程并释放锁,直到被唤醒。即另一个线程获得那把锁,并调用锁对象的 notify 或 notifyAll 方法
- notify 调用后,wait 方法并不是立马往后执行,它需要重新获取锁
- wait 调用后会把当前线程放进一个 wait 集合中,那个集合并不是有序的
利用 wait 的阻塞特性,我们可以用它来实现循环打印 ABC ;可以使用三把锁来实现,还需要一个变量来控制当前应该打印谁,当前如果不是打印这个值时,调用 wait,如果是打印这个值,就打印这个值并切换成一个打印变量,同时唤醒下一个打印,伪代码如下:
完整代码 ABCThreadWait
在 这个地方
char currentChar = 'A';
Object lockA = new Object();
Object lockB = new Object();
Object lockC = new Object();
ThreadA
synchronized(lockA){
// 先获取 A 锁,可能马上就要调用 wait 方法
// 因为这里只有三个线程,所以用 if 和 while 是一样的,建议用 while
if(currentChar != 'A'){
lockA.wait();
}
print('A');currentChar = 'B';
//然后唤醒 B ,需要先获取B 锁
synchronized(lockB){
lockB.notify();
}
}
ThreadB
synchronized(lockB){
if(currentChar != 'B'){
lockB.wait();
}
print('B');currentChar = 'C';
synchronized(lockC){
lockC.notify();
}
}
ThreadC
synchronized(lockC){
if(currentChar != 'C'){
lockC.wait();
}
print('C');currentChar = 'A';
synchronized(lockA){
lockA.notify();
}
}
这里可以精简为使用一个线程类,使用不同的线程实例来实现,但还是使用的三把锁,每个线程都需要先获取自身的锁,然后判断是否需要打印,如果不是当前字符则释放锁;是当前字符就打印字符,在打印完后获取下一个打印的锁,把下一个打印线程唤醒。伪代码如下:
完整代码 ABCThreadWaitOneThreadClass
在 这个地方
synchronized (lockSelf){
if(printChar != currentPrintChar){
try {
lockSelf.wait();
} catch (InterruptedException e) {e.printStackTrace();}
}
// 打印当前线程字符
System.out.print(printChar);
// 切换下一个线程,并切换状态
if(currentPrintChar == 'A'){currentPrintChar = 'B';}
else if(currentPrintChar == 'B'){currentPrintChar = 'C';}
else if(currentPrintChar == 'C'){currentPrintChar = 'A';}
//唤醒下一个线程
synchronized (lockNext) {
lockNext.notify();
}
}
当然也可以使用一把锁来实现,这需要用到 Lock + Condition ,关于 Lock 和 Condition 见这篇文章
使用 Condition 的 await signal signalAll 时,同样需要获得 Lock 锁,其它特性等同于 wait notify notifyAll
其实仔细看这道题,永远只有一个线程在打印,照理论来说只需要一把锁即可,上面需要有多把锁的原因是一个锁只有一个等待队列 ,并且 notify 也是随机唤醒的。而每个 Condition 会带一个等待队列,所以用 Condition 只要一把锁就可以了,减轻了代码的复杂度,多锁情况很容易造成死锁。
使用 Lock + Condition 的完整代码 ConditionABC
在 这个地方 ,这是用多个线程类实现的,当然也可以用单个线程类多个线程实例来实现,这里就不再写了。
借用一篇写得挺不错的博文 ,请一定耐心把它读完再接着往下读 你真的懂 wait notify notifyAll 吗
文章中的源码QueueUseWaitNotify
在 这个地方
这个图不错,收藏了
文章中有一个地方说得挺好,就是面试常问的 notify 和 notifyAll 的区别
青铜玩家会一脸纯真的看着面试官,就是唤醒一个和唤醒一堆啊,但它两真正的区别是 notifyAll 调用后,会把所有在 Wait Set 中的线程状态变成 RUNNABLE 状态,然后这些线程再去竞争锁,获取到锁的线程为 Run 状态,没有获取到锁的线程进入 Entry Set 集合中变成 Block 状态,它们只需要等到上个线程执行完或者 wait 就可以再次竞争锁而无需 notify ; 而 notify 方法只是照规则唤醒 Wait Set 中的某一个线程,其它的线程还是在 Wait Set 中。
文章中说到的为什么 wait 要写在 for 循环中是因为 wait 是释放了锁,然后阻塞,等到下次唤醒的时候,在多个生产者多个消费者的情况下,有可能是被 “同类” 唤醒的,所以需要再去检查下状态是否正确。
文章中有一个地方没有说明白 ,这里再解释下,就是那个使用 notfiy 会带来死锁的问题,个人理解,如有偏差望指正
当有多个消费者和多个生产者的时候,这时正好在消费,所以生产者是在 Wait Set 中,可能还有其它消费者也在 Wait Set 中,因为是 notify 而不是 notfiyAll 嘛,所以消费者有可能一直 notify 的都是另一个消费者,刚好这时 buffer 空了,正好所有消费都 wait 了而没能及时 notify 生产者,这时 Wait Set 中四目相望造成死锁。
文章最后有一个评论说可以生产者用一把锁,消费者用一把锁,这里也有实现 QueueUseWaitNofiy2
可以使用 Condition 做更好的实现,只使用一把锁,这里本身也只需要一把锁就可以了,具体实现见代码 QueueUseCondition
一点小推广
创作不易,希望可以支持下我的开源软件,及我的小工具,欢迎来 gitee 点星,fork ,提 bug 。
Excel 通用导入导出,支持 Excel 公式
博客地址:https://blog.csdn.net/sanri1993/article/details/100601578
gitee:https://gitee.com/sanri/sanri-excel-poi
使用模板代码 ,从数据库生成代码 ,及一些项目中经常可以用到的小工具
博客地址:https://blog.csdn.net/sanri1993/article/details/98664034
gitee:https://gitee.com/sanri/sanri-tools-maven
wait notify notifyAll await signal signalAll 的理解及示例的更多相关文章
- Java并发学习 & Executor学习 & 异常逃逸 & 同步互斥Best Practice & wait/notify, conditon#await/signal
看了这篇文章:http://www.ciaoshen.com/2016/10/28/tij4-21/ 有一些Java并发的内容,另外查了一些资料. 朴素的Thread 首先,Java中关于线程Thre ...
- JUC在深入面试题——三种方式实现线程等待和唤醒(wait/notify,await/signal,LockSupport的park/unpark)
一.前言 在多线程的场景下,我们会经常使用加锁,来保证线程安全.如果锁用的不好,就会陷入死锁,我们以前可以使用Object的wait/notify来解决死锁问题.也可以使用Condition的awai ...
- 使用ReentrantLock和Condition来代替内置锁和wait(),notify(),notifyAll()
使用ReentrantLock可以替代内置锁,当使用内置锁的时候,我们可以使用wait() nitify()和notifyAll()来控制线程之间的协作,那么,当我们使用ReentrantLock的时 ...
- java 多线程 22 :生产者/消费者模式 进阶 利用await()/signal()实现
java多线程15 :wait()和notify() 的生产者/消费者模式 在这一章已经实现了 wait/notify 生产消费模型 利用await()/signal()实现生产者和消费者模型 一样 ...
- java 并发——理解 wait / notify / notifyAll
一.前言 前情简介: java 并发--内置锁 java 并发--线程 java 面试是否有被问到过,sleep 和 wait 方法的区别,关于这个问题其实不用多说,大多数人都能回答出最主要的两点区别 ...
- “全栈2019”Java多线程第三十三章:await与signal/signalAll
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- Java Object对象中的wait,notify,notifyAll的理解
wait,notify,notifyAll 是定义在Object类的实例方法,用于控制线程状态,在线程协作时,大家都会用到notify()或者notifyAll()方法,其中wait与notify是j ...
- notify notifyAll 死锁
从一个死锁分析wait,notify,notifyAll 泡芙掠夺者 关注 2017.08.24 22:00* 字数 1361 阅读 249评论 3喜欢 7赞赏 1 本文通过wait(),notify ...
- 使用Object的wait,notify,notifyAll做线程调度
我们知道java中的所有类的祖先都是Object,Object类有四个个方法wait(),wait(long timeout),notify(),notifyAll(),这四个方法可以用来做线程的调度 ...
随机推荐
- 大数据之路week01--自学之集合_1(Collection)
经过我个人的调查,发现,在今后的大数据道路上,集合.线程.网络编程变得尤为重要,为什么? 因为大数据大数据,我们必然要对数据进行处理,而这些数据往往是以集合形式存放,掌握对集合的操作非常重要. 在学习 ...
- [视频演示].NET Core开发的iNeuOS物联网平台,实现从设备&PLC、云平台、移动APP数据链路闭环
目 录 1. 概述... 1 2. 登陆信息... 2 3. 设备驱动... 3 4. 组态建模... 3 5. 手机APP. 5 6. ...
- jwt 实践应用以及特殊案例思考
JSON Web Token 是 rfc7519 出的一份标准,使用 JSON 来传递数据,用于判定用户是否登录状态. jwt 之前,使用 session 来做用户认证. 以下代码均使用 javasc ...
- 力扣(LeetCode)亲密字符串 个人题解
给定两个由小写字母构成的字符串 A 和 B ,只要我们可以通过交换 A 中的两个字母得到与 B 相等的结果,就返回 true :否则返回 false . 示例 1: 输入: A = "ab& ...
- vim可视化模式
进入:v 移动光标选中 c剪切.y复制(自动退出v模式,进入插入模式) p粘贴
- 开始逆向objc基础准备(一)简单认识一下arm32,以及与x86汇编指令类比
ARM32体系中有31或33个通用寄存器,没有特定的某种态下有r0-r15一共16个寄存器,快速中断态下有另一组r8-r12备份寄存器,在用户态和系统态之外其它态下都各自有一组r13-r14备份寄存器 ...
- hopper反汇编工具的逆向伪代码功能并不理想
hopper的逆向代码功能并不如想象中那么好,尤其是在逆向c++代码时.对于从ObjC进入iOS开发又不太清楚运行时的人员来说,hopper可以将反汇编码输出成[obj selector:what]这 ...
- 数据库05 使用percona软件来进行数据备份
1.为什么要与用percona来备份 常见的MySQL备份工具 —跨平台性差 —备份时间长.冗余备份.浪费存储空间 mysqldump备份缺点: —效率较低.备份与还原速度慢,锁表(即备份数据库中的一 ...
- 笔记本进入BIOS设置
转眼间,到大三了. 在学习<Red Hat Linux 服务器搭建与管理>这门课时,刚开学第一节,就是虚拟机,但是最烦恼的是我们笔记本电脑的默认设置,它把虚拟化给禁止了. 1,首先,我们需 ...
- 洛谷P2634 聪聪可可 (点分治)
###题目链接### 题目大意: 给你一棵树,假如树上两点间的距离是 3 的倍数 的点对有 s 对,则输出最简分数 s/n ,其中 n 表示所有整棵树的点对总数. 分析: 1.显然,可以采用点分治. ...