当前位置: 首页 > web >正文

wait,notify/notifyAll的使用及实现原理

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
http://www.xdnf.cn/news/11181.html

相关文章:

  • ObjectDock
  • Java 10正式发布,带来了这些新特性
  • Coqui:创建逼真的生成式人工智能语音
  • 在网络安全领域,比较牛的中国黑客有哪些?
  • git clone 遇到问题:fatal: unable to access ‘https://github.comxxxxxxxxxxx‘: Failed to connect to xxxxxxx
  • iOS开发进阶(一):iOS原生开发环境搭建步骤详解_ios编译环境搭建
  • 关于connectionstring字符串的那点事
  • Python OpenCV 3.x 示例:6~11
  • cache介绍及问题解决
  • 原始数据哪里找?这些网站要用好!200个国内外经济/金融/行研/咨询数据网站大全(附链接)...
  • Linux系统中rpm命令用法详解
  • 华为3108raid安linux,华为服务器 RAID卡配置 SR430 LSISAS3108(Legacy/Dual模式)传统模式
  • CorelDRAW X4 SP2 简体中文正式版精简增强版
  • 简易日志告警系统
  • 群晖上搭建teamspeak3语音服务器
  • 如何实现两地星三角启动
  • Event事件学习实用路线(9)——Event事件之键盘事件——案例:键盘操作元素位置——案例:键盘事件组合键控制
  • 【交替方向乘子方法】ADOM: 基于ADMM的遥感图像条纹噪声去除优化模型(Matlab代码实现)
  • 一键自动化博客发布工具,用过的人都说好(简书篇)
  • mom.exe进程什么?
  • 下载和中文攻略之更胜黎明前的琉璃色 夜明け前より瑠璃色な
  • 安焦删除贴 牛人纷纷出现(1)
  • 无需公网IP搭建的web服务器,简单易上手
  • PHP程序员上相亲节目,结果遭女嘉宾瞬间全灭灯
  • 云服务器防 DDoS 攻击的几种方法策略分享
  • 浏览器缓存相关的HTTP头介绍:Expires,Cache-Control,Last-Modified,ETag
  • fullPage.js 学习
  • superoneclick 2.2_2.2.1定量遥感综述
  • 局域网有一台计算机网络慢,局域网网速变慢的五种解决办法
  • 量化交易:Dual Thrust策略