1、介绍

java中的wait/notify的等待唤醒机制可以实现线程间的通信,wait使当前线程进入等待,该操作会使线程阻塞,直到其它线程唤醒(调用notify、notifyAll方法)它才可继续执行。

  • wait()、notify()/notifyAll()都是定义在 Object类的final方法,不支持重写。
  • wait()、notify()/notifyAll()都和对象监视器密切相关,所以这几个方法都需和synchronized搭配使用。
  • wait()方法在已获取锁的前提下,使当前线程阻塞,释放当前锁,等待其它线程唤醒。
  • notify()唤醒此对象监视器上阻塞等待的单个线程,不释放锁,被唤醒的线程参与到锁的竞争中。
  • notifyAll()唤醒此对象监视器上阻塞等待的所有线程,不释放锁,被唤醒的所有线程参与到锁的竞争中。

2、使用案例

案例壹:使用两个线程交替打印1-26,a-z

/*** 代码*/
public class Demo {public synchronized void printNum() {for(int i=1;i<=26;i++){this.notify();System.out.print(i);try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}public synchronized void printAz(){char[] arr = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};for(int i=0;i<arr.length;i++){this.notify();System.out.print(arr[i]);try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {final Demo demo = new Demo();new Thread(()->{demo.printNum();}).start();new Thread(()->{demo.printAz();}).start();}
}/*** 运行结果*/
1a2b3c4d5e6f7g8h9i10j11k12l13m14n15o16p17q18r19s20t21u22v23w24x25y26z

案例贰:使用三个线程交替打印1-26,a-z,A-Z

/*** 代码*/
public class Demo {public void printNum() {for(int i=1;i<=26;i++){synchronized ("AZ"){synchronized ("num"){System.out.print(i);"num".notify();}try {"AZ".wait();} catch (InterruptedException e) { e.printStackTrace(); }}}}public void printAz(){char[] arr = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};for(int i=0;i<arr.length;i++){synchronized ("num"){synchronized ("az"){System.out.print(arr[i]);"az".notify();}try {"num".wait();} catch (InterruptedException e) { e.printStackTrace(); }}}}public void printAZ(){char[] arr = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};for(int i=0;i<arr.length;i++){synchronized ("az"){synchronized ("AZ"){System.out.print(arr[i]);"AZ".notify();}try {"az".wait();} catch (InterruptedException e) { e.printStackTrace(); }}}}public static void main(String[] args) throws InterruptedException {Demo demo = new Demo();new Thread(()->{demo.printNum();}).start();TimeUnit.SECONDS.sleep(1);new Thread(()->{demo.printAz();}).start();TimeUnit.SECONDS.sleep(1);new Thread(()->{demo.printAZ();}).start();}
}/*** 运行结果*/
1aA2bB3cC4dD5eE6fF7gG8hH9iI10jJ11kK12lL13mM14nN15oO16pP17qQ18rR19sS20tT21uU22vV23wW24xX25yY26zZ

3、实现原理

java线程的五种状态图解:

重量级锁通过对象内部的监视器(monitor)实现,监视器内部记录了获取当前监视器的线程和进入锁的次数等信息。monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的),内部记录了处于等待blockd状态的线程列表和处于锁定blockd状态的线程列表。

ObjectMonitor() {_header       = NULL;_count        = 0; _waiters      = 0,_recursions   = 0;_object       = NULL;_owner        = NULL;_WaitSet      = NULL; //处于等待blockd状态的线程,会被加入到_WaitSet_WaitSetLock  = 0 ;_Responsible  = NULL ;_succ         = NULL ;_cxq          = NULL ;//处于锁定blockd状态的线程,会被加入到_cxqFreeNext      = NULL ;_EntryList    = NULL ; _SpinFreq     = 0 ;_SpinClock    = 0 ;OwnerIsThread = 0 ;}

monitor获取释放图解:

加入wait、notify后monitor获取释放图解:

4、注意事项

根据monitor获取释放图解,锁对象调用wait、notify/notifyAll前提是当前线程已经获取到此锁对象。

若当前线程获取到a对象锁,使用b对象调用wait、notify/notifyAll,则会报运行时异常。

演示1(正常)

/*** 代码*/
public class Demo {public static void main(String[] args) throws InterruptedException {new Thread(()->{synchronized ("lock1"){try {//当前线程已获取到字符串lock1的锁对象,可以释放。"lock1".wait();System.out.println("lock1锁住的线程已被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}}).start();TimeUnit.SECONDS.sleep(1);synchronized ("lock1"){//当前线程已获取到字符串对象lock1的锁对象,可以唤醒。"lock1".notify();}}
}

演示2(正常)

/*** 代码*/
public class Demo {public static void main(String[] args) throws InterruptedException {new Thread(()->{synchronized ("lock1"){synchronized ("lock2"){try {当前线程已获取到字符串lock1、lock2的锁对象,lock1可以释放。"lock1".wait();System.out.println("lock1锁住的线程已被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}}}).start();TimeUnit.SECONDS.sleep(1);synchronized ("lock1"){//当前线程已获取到字符串对象lock1的锁对象,可以唤醒。"lock1".notify();}}
}

演示3(异常)

/*** 代码*/
public class Demo {public static void main(String[] args){new Thread(()->{synchronized ("lock1"){try {//当前线程已获取到字符串对象lock1的锁对象,释放lock2异常TimeUnit.SECONDS.sleep(1);"lock2".wait();} catch (Exception e) {e.printStackTrace();}}}).start();new Thread(()->{synchronized ("lock2"){try {TimeUnit.SECONDS.sleep(1);//当前线程已获取到字符串对象lock2的锁对象,释放lock1异常"lock1".wait();} catch (Exception e) {e.printStackTrace();}}}).start();}
}/*** 运行结果报错*/
java.lang.IllegalMonitorStateException
java.lang.IllegalMonitorStateException

wait,notify/notifyAll的使用及实现原理相关推荐

  1. JAVA线程间协作:wait.notify.notifyAll

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

  2. java wait notifyall_Java中的wait/notify/notifyAll

    方法 java.lang.Object public final native void wait() throws InterruptedException; public final native ...

  3. 终于搞懂了sleep/wait/notify/notifyAll

    作者:acupt,专注Java,架构师社区合伙人! sleep/wait/notify/notifyAll分别有什么作用?它们的区别是什么?wait时为什么要放在循环里而不能直接用if? 简介 首先对 ...

  4. [转]java 为什么wait(),notify(),notifyAll()必须在同步方法/代码块中调用?

    在 Java中,所有对象都能够被作为"监视器monitor"--指一个拥有一个独占锁,一个入口队列和一个等待队列的实体entity. 所有对象的非同步 方法都能够在任意时刻被任意线 ...

  5. sleep() wait() notify/notifyAll() 的区别

    看了一下线程这方面的知识,也结合了一些 面试的题 总结如下: 一些基础的就不说了,主要说一下这里面的几个方法,也是object的通用方法. sleep()     wait()     notify/ ...

  6. wait/notify/notifyAll在Object类中

    wait/notify/notifyAll在Object类中 因为我们在使用synchronized锁 对象锁可以是任意对象,所以wait/notify/notifyAll需要放在Object类中. ...

  7. 线程的状态转换、sleep()、wait()、yeild()、终止线程的方法、线程之间的协作(join()、wait() notify() notifyAll()、await() signal() )

    1.线程的状态转换 1.1 新建(New) 创建后尚未启动 1.2 可运行(Runnable) 可能正在运行,也可能正在等待 CPU 时间片. 包含了操作系统线程状态中的 Running 和 Read ...

  8. java多线程notifyall_Java多线程:线程状态以及wait(), notify(), notifyAll()

    1. wait(), notify(), notifyAll()等方法介绍 在Object.java中,定义了wait(), notify()和notifyAll()等接口.wait()的作用是让当前 ...

  9. concurrent: wai notify notifyAll

    转载自:http://sishuok.com/forum/blogPost/list/3674.html 通常,多线程之间需要协调工作.例如,浏览器的一个显示图片的线程displayThread想要执 ...

最新文章

  1. PHP查看PECL模块包含的函数
  2. 机器学习从零开始-常见算法手推pure python
  3. java项目部署到linux上面,把我的Java项目部署到Linux系统
  4. JAVA-用栈机制实现单词逆序排列
  5. 谁能主宰服务器市场?Linux 之父和 Redis 之父有分歧了
  6. 【优化】如何检测移动端 CPU 以及内存占用率
  7. ICCV7 For AVR 使用教程 Atmega16 单片机
  8. 四维图新地图坐标_移动端地图技术分享
  9. 百度云OCR文字识别
  10. bzoj4516 [Sdoi2016]生成魔咒
  11. listing directory /
  12. 苹果中国首家直营店选址北京三里屯
  13. 《MAC OS X 技术内幕》读书笔记第一章:MAC OS X的起源
  14. web测试中的测试点和测试方法总结
  15. GMap.NET入门详细教程【4】--------为控件添加事件,在鼠标单击时打点
  16. 关于VeryCD下载的关闭
  17. 【FFmpeg如何判断HDR视频,SDR视频】
  18. python爬虫之Ajax动态加载数据抓取--豆瓣电影/腾讯招聘
  19. 网络字体(Web font)文件格式及兼容性说明
  20. P5.js开发之——环境

热门文章

  1. java static加载顺序
  2. java键盘输入字符串静态变量_Java 中为什么样在静态方法中可以调用的方法、可以使用的成员变量必须是静态的?老师说字符串常量和静态变量放在data segment中...
  3. 用Rational RequisitePro写用例规约(Use Case Specification)的心得
  4. PHP json_encode 与 json_decode 空格问题
  5. Vue学习(二)-胡子({{}})语法
  6. 在 windows 环境下,编写一个批处理程序(算命大师.bat)
  7. Spring中的定时任务fixedRate和fixedDelay和cron的区别
  8. KLT光流跟踪特征点对
  9. 第二章软件项目需求管理
  10. 小程序的 生命周期回调函数onLoad,onReady,onShow