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

【JavaEE初阶】深入理解wait和notify以及线程饿死的解决

前言:

🌈上期博客:【JavaEE初阶】深入解析死锁的产生和避免以及内存不可见问题-CSDN博客

🔥感兴趣的小伙伴看一看小编主页:【JavaEE初阶】深入解析死锁的产生和避免以及内存不可见问题-CSDN博客

⭐️小编会在后端开发的学习中不断更新~~~  

🥳非常感谢你的支持

 

目录

📚️1.引言

 📚️2.wait和notify

2.1wait作用

 2.2使用场景和线程饿死

 2.3wait如何使用

1.wait的锁对象

2.wait必须在synchronized内

3.调用wait的锁对象

4.notify的使用

📚️3.理解执行顺序

📚️4.注意事项

4.1wait不同的方法

4.2notify唤醒wait

4.3wait和sleep的区别

📚️5.总结


 

📚️1.引言

 OK啊!!!小伙伴们,在上期继小编讲解过死锁的问题后,本期将开始理解wait和notify的线程问题,那么废话不多说,直接步入正题,go go go~~~;

且听小编讲解,包你学会!!! 

 📚️2.wait和notify

2.1wait作用

这里的作用和上期讲解过的join有异曲同工之妙,都是在多线程随机调度中,通过引入wait和notify来实现干预不同线程的执行顺序,让后执行的线程不被调度,让先执行的线程把对应的代码执行完

 2.2使用场景和线程饿死

例如一个现实场景:取钱问题~~~

这里的ATM可能是没有钱的,那么此时当一个人去取完钱后,解锁后,其他滑稽老铁进入锁的竞争,但是此时刚刚取钱的滑稽老铁也会参与到锁的竞争,就导致此时壹号滑稽老铁由于竞争,和ATM没有钱,而一直反复获取锁,而导致其他的滑稽不能拿到锁,这就是“线程饿死” 

注意:由于已经拿到锁的滑稽老铁会处于RUNNABLE状态,但是其他线程处于阻塞状态,那么此时就是“近水楼台先得月” ,其他线程需要进行唤醒,才能参与到锁的竞争,那么就会导致壹号滑稽老铁更容易获取到锁~~~

此时就要运用到wait和notify了,让线程满足条件进行加锁执行,若不满足条件,那么就进入wait,直到满足条件后被notify唤醒~~~

下面是一个伪代码

 public static void main(String[] args) {while (true){synchronized (){if (ATM中有钱){进行取钱的操作 break;}else {进入等待;wait(){直到有钱然后被唤醒}}}}}

所以这就是我们所需要看到的情况;

注意:wait在这里做了三件事情

1.释放锁,让其他线程获取锁

2.让线程进入阻塞状态

3.被notify唤醒后,重新参与锁的竞争

 2.3wait如何使用

1.wait的锁对象

这里和加锁是一样的都是需要锁对象,当然这里的锁对象是可以为任何对象的,小编就不再过多解释

2.wait必须在synchronized内

此时如果我们将wait写到synchronized之外,代码如下:

 public static void main(String[] args) throws InterruptedException {Object object = new Object();System.out.println("wait 之前");object.wait();System.out.println("wait 之后");}

输出打印日志:

wait 之前
Exception in thread "main" java.lang.IllegalMonitorStateExceptionat java.lang.Object.wait(Native Method)at java.lang.Object.wait(Object.java:502)at thread.testDemo17.main(testDemo17.java:7)

可以看到此时程序报错了;

注意:wait使用的前提是存在锁,所以在使用wait之前线程必须先获取锁。

3.调用wait的锁对象

在调用wait的锁对象必须和加锁的synchronized是同一个锁对象,所以wait解锁是synchronized的锁,重新唤醒后加锁也是synchronized的锁,代码如下:

public static void main(String[] args) throws InterruptedException {Object object = new Object();synchronized (object) {System.out.println("wait 之前");object.wait();System.out.println("wait 之后");}}

此时输出就只有wait之前,因为此时线程进入阻塞了,咱们打开jconsole可以看出到此时的线程状况,如图所示:

此时代码进入WAITING状态~~~

4.notify的使用

注意此时,notify使用与其他的线程,并且调用notify的锁对象,也必须和调用wait的锁对象必须是一样的,唤醒的即对应锁对象调用的wait方法的线程;

📚️3.理解执行顺序

这里需要注意,在使用wait和notify后的代码是如何进行执行的,代码实例如下:

public static void main(String[] args) {Object lock=new Object();//创建线程1Thread t1=new Thread(()->{synchronized (lock){System.out.println("wait之前");try {lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("wait之后");}});Thread t2=new Thread(()->{try {Thread.sleep(1000);//保证线程1能够成功加上锁} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (lock){System.out.println("notify之前");lock.notify();System.out.println("notify之后");}});t1.start();t2.start();}

注意:小编这里为了保证线程1成功加上锁,那么在执行线程2时,就会进入休眠;还有wait和sleep一样,都有可能会被interrupt提前唤醒,所以这里在写wait时,需要进行异常抛出;

输出结果:

那么此时的执行顺序就是如下的:

1.首先线程1开始执行,加锁后执行打印“wait之前”,然后进入等待状态,释放锁并进入阻塞状态;

2.线程2开始执行,获取到锁后打印“notify之前”,然后唤醒线程一;

3.由于线程1处于WAITING状态,被唤醒后,由于锁的竞争会处于一个小小的阻塞,在获取锁这个阶段需要时间开销,所以先打印“notify之后”

4.最后线程1获取到锁后,最后执行最后的打印“wait之后”

📚️4.注意事项

4.1wait不同的方法

在进行wait方法的调用时,可以看到有三个其他的版本,如下图:

第一种:即死等,若没有进程进行唤醒操作,此时就会导致这个线程不再执行

第二种:即超时时间,单位为ms,若没有线程唤醒,那么到了这个时间段,就不再进行等待了;

第三种:即纳秒级别的时间,由于系统精度,可能无法准确执行,且很少使用;

4.2notify唤醒wait

1.若两个锁对象调用的这两个方法,如果锁对象是不一样的,那么就无法进行唤醒;

2.若右两个锁对象调用wait,那么调用notify的锁对象,要唤醒对应的wait,若两个wait的锁对象是一样的,那么随机唤醒

3.notifyAll用于唤醒对应线程上的所有线程;注意:被唤醒的线程中,多个wait的执行,导致锁的竞争,那么此时那个限制性,那个后执行是不确定的;

4.3wait和sleep的区别

1.这里的wait也是带有时间的超时时间的wait方法,sleep也是一样的带有时间的,那么就是到时间就会继续执行;

2.wait和sleep虽然时间没有到,但是任然可以被提前唤醒;wait是通过notify进行唤醒,而sleep是通过interrupt进行提前唤醒;

3.使用wait,是不知道等待的时间的前提下使用的,所谓的超时时间只不过是一个“兜底时间”,而sleep是要知道时间的前提下才使用,虽然也能够被提前唤醒,但是这个是一个不正常的业务流程,(异常唤醒,这是说明代码出现了BUG了)

📚️5.总结

💬💬小编本期讲解了关于wait的使用方法,对应的notify的操作,以及线程饿死的场景演示;当然还有在使用wait和notify时的使用注意事项,还附上了对应代码供uu们参考参考~~~

🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!


💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

                                               😊😊  期待你的关注~~~


http://www.mrgr.cn/news/38208.html

相关文章:

  • MySQL - 单表查询
  • QQ机器人搭建
  • docker build 有时候不展示命令的输出情况,怎么办?
  • 尚硅谷vue3+TypeScript笔记大全
  • 五.海量数据实时分析-FlinkCDC+DorisConnector实现数据的全量增量同步
  • 递归算法详解
  • 如果只能保留一个复制粘贴软件,那一定是它pastemate
  • LeetCode讲解篇之5. 最长回文子串
  • 笔试练习day6
  • Mysql优化(常见优化)
  • 基于Java的停车场管理微信小程序 停车场预约系统【源码+文档+讲解】
  • [大语言模型-论文精读] 大语言模型是单样本URL分类器和解释器
  • <<迷雾>> 第 2 章 用电来表示数 示例电路
  • 深度学习与数学归纳法
  • springboot+vue+java校园共享厨房菜谱系统
  • feign的全局拦截器和局部拦截器
  • 为啥我关不掉页面?Chrome扩展开发中的那些坑
  • LLM大模型学习:致AI新手掌握这些经验,助你少走三年弯路!
  • 纠删码参数自适应匹配问题ECP-AMP实验方案(一)
  • Java实现找色和找图功能