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(),这四个方法可以用来做线程的调度 ...
随机推荐
- IPv6笔记-地址结构与分类
1.地址基础 IPv6地址由被划分为8个16位块的128位组成. 然后将每个块转换为由冒号符号分隔的4位十六进制数字. 2001::3238:00E1:0063:0000:0000:FEFB 每一块多 ...
- JavaScript中BOM与DOM的使用
BOM: 概念:Browser Object Model 浏览器对象模型 将浏览器的各个组成部分封装成对象. 组成: Window:窗口对象 Navigator:浏览器对象 Screen:显示器屏幕对 ...
- ios沙盒机制---基本数据类型的存取和文件的基本操作
沙盒快速存储及读取 存储: [[NSUserDefaults standardUserDefaults] setObject:@"abc" forKey:@"1&quo ...
- Reporting service个人使用经验
(此文章是从自己写的Word文档里复制过来的,图没有了,文字也有些乱,凑合看吧) 部署环境 Windows server 2012 R2 软件环境 安装完整的sqlsever2012,IIS服务 需要 ...
- EFCore批量操作,你真的清楚吗
背景 EntityFramework Core有许多新的特性,其中一个重要特性便是批量操作. 批量操作意味着不需要为每次Insert/Update/Delete操作发送单独的命令,而是在一次SQL请求 ...
- 我的第一个 60 k+ Star Java开源项目
JavaGuide([Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识):https://github.com/Snailclimb/JavaGuide. 人生总有各种各样的 ...
- Windows服务的删除与添加
Windows服务的删除与添加 以管理员身份运行 CMD 服务的删除 sc delete 服务名称 服务添加 sc create 服务名称 binpath="服务EXE文件所在的目录&quo ...
- nyoj 599-奋斗的小蜗牛 (double ceil(); (temp - 1) / 5)
599-奋斗的小蜗牛 内存限制:64MB 时间限制:1000ms 特判: No 通过数:0 提交数:96 难度:1 题目描述: 传说中能站在金字塔顶的只有两种动物,一种是鹰,一种是蜗牛.一只小蜗牛听了 ...
- nyoj 168-房间安排 (贪心)
168-房间安排 内存限制:64MB 时间限制:3000ms 特判: No 通过数:33 提交数:71 难度:2 题目描述: 2010年上海世界博览会(Expo2010),是第41届世界博览会.于20 ...
- Pashmak and Buses(构造)
题目链接:http://codeforces.com/problemset/problem/459/C 题意:n个人, k辆车, d天,每天将所有 任意人安排到k辆车, 问怎样安排, 可时不存在 2人 ...