Java多线程:waitnotify原理剖析

Java多线程wait/notify原理

一、引入wait/notify

回顾:在之前的学习中,当我们创建一个对象后,synchronized给对象上锁,JVM会给对象头关联一个Monitor对象,这个Monitor由三部分组成。
一是Owner对象,里面存储的是创建该对象的线程
二是EntryList,想试图获取该对象资源的其它堵塞线程队列
三是WaitSet,存储的是放弃对象锁的线程

在这里插入图片描述

  • Owner线程中的锁对象,如果发现条件不满足,调用wait()方法,既可以进入到WaitSet变为WAITING状态
  • EntryList下和WaitSet下的线程都属于堵塞状态,不占用CPU时间片
  • EntryList下的线程会在Owner是释放锁时被唤醒
  • WaitSet下的线程会在Owner线程调用notify或notifyAll时被唤醒,但是唤醒后并不意味立刻获得锁,需要进入EntryList重新竞争锁

API介绍

  • wait() 会让obj对象由拥有锁到暂时放弃锁,进入到waitset中
  • notify() 会唤醒在waitset中的线程,然后需要进入EntryList重新竞争锁
  • notifyAll() 会唤醒所有在waitset中的线程,然后需要进入EntryList重新竞争锁

wait(long n)wait()的区别是,前者是有时限的等待,时间到后自己进入EntryList中,后者如果没有notify()notifyAll()唤醒则进入永久等待。

wait/notify 的正确姿势

开始之前先看看sleep()wait()方法有什么区别?

  • sleep()是Thread的静态方法,而wait()方法是Object类下的方法
  • sleep()方法不需要与synchronized配合使用,但是wait()方法必须和sysnchronized配合才能使用
  • 调用sleep()方法,对象不会释放锁,调用wait()方法时对象会释放锁
  • 相同点就是调用两个方法后,线程的状态都是TIMED_WAITING状态

wait/notify与锁关系的探究

wait()时是否需要持有锁? notify()是否需要持有锁?先说答案:都需要持有锁。

wait需要持有锁的原因是,你肯定需要知道在哪个对象上进行等待,如果不持有锁,将无法做到对象变更时进行实时感知通知的作用。与此同时,为了让其他线程可以操作该值的变化,它必须要先释放掉锁,然后在该节点上进行等待。不持有锁而进行wait,可能会导致长眠不起。而且,如果不持有锁,则当wait之后的操作,都可能是错的,因为可能这个数据已经过时,其实也叫线程不安全了。总之,一切为了安全,单独的wait做不成这事。

notify需要持有锁的原因是,它要保证线程的安全,只有它知道数据变化了,所以它有权力去通知其他线程数据变化。而且通知完之后,不能立即释放锁,即必须在持有锁的情况下进行通知,否则notify后续的工作的线程安全性将无法保证,尽量它是在lock的范围内,但却因为锁释放,将导致不可预期的结果。而且在notify的时候,并不能真正地将对应的线程唤醒,即不能从操作系统层面唤醒线程,因为此时当前通知线程持有锁,而此时如果将其他等待线程唤醒,它们将立即参与到锁的竞争中来,而这时的竞争是一定会失败的,这可能会导致被唤醒的线程立即又进入等待队列,更糟糕的是它可能再也不会被唤醒 了。所以不能将在持有锁的时,将对应的线程真正唤醒,我们看到的notify只是从语言上下文级别,将它从等待队列转移到同步队列而已(就是从上面的WaitSet进入EntryList),对此操作系统一无所知

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JermeryBesian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值