这几天抽空整理了一下java中wait/notify/notifyall的知识点,如果有写的不好的地方,欢迎各位留言
1、wait()/notify()/notifyAll() 方法在使用时, 必须使用同一个对象的锁。
2、wait()方法执行时,会释放锁,同时线程挂起
3、notify()/notifyAll()方法执行的时候,并不会立刻唤醒,因为(notify()/notifyAll() 并不释放锁),而是退出同步块之后,才唤醒
4、notify()默认唤醒策略是:先进入wait的线程先被唤醒 (可以自己设置策略)
5、notifyAll()默认唤醒策略是:采用LIFO策略 (可以自己设置策略)
6、简单介绍ObjectMonitor+ObjectWaiter (这里只是单纯介绍一下 这两个类是干啥的,如需深入探求则需要查询相关资料和jdk源码)
6.1 ObjectMonitor介绍:
6.1.1 在Hotspot虚拟机中,monitor采用ObjectMonitor实现
6.1.2 每个线程都有两个ObjectMonitor对象列表,分别为free和used列表,如果当前free列表为空,线程将向global中 listLock 请求分配ObjectMonitor
6.1.3 ObjectMonitor有_WaitSet 和 _EntryList(or _cxq)队列, 用来保存ObjectWaiter列表,_owner表示获得ObjectMonitor对象的线程
(此图来源网络)
_WaitSet:处于wait状态的线程,会被加入到此队列中
_EntryList: 处于等待锁block状态的线程,会被加入到此队列中
6.2 ObjectWaiter介绍
6.2.1 ObjectWaiter是一个环型双向链表结构
6.2.2 ObjectWaiter是通过由当前Thread + TState等数据构造而来的
7、wait()具体做了什么事情?
7.1 由当前Thread包装为ObjectWaiter,此时TState=TS_WAIT
7.2 ObjectWaiter对象放入到ObjectMonitor的_WaitSet 队列中
7.3 释放锁,并让当前线程挂起 (使用park/unpark,会释放CPU)
8、notify()具体做了什么事情?
8.1 如果当前_waitSet队列为空,即表示没有线程在等待,那么直接返回
8.2 从_waitSet队列中 取出第一个ObjectWaiter节点
8.3 根据Policy的不同,将这个ObjectWaiter放入到_EntryList队列中
或通过Atomic::cmpxchg_ptr 指令进行自旋操作cxq
8.3.1
Policy==0: 放入_EntryList队列的排头位置
Policy==1: 放入_EntryList队列的末尾位置
Policy==2: _EntryList队列为空就放入_EntryList,否则放入_cxq队列的排头位置
Policy==3: 放入_cxq队列的末尾位置
Policy==其他值: 立刻唤醒ObjectWaiter对应的线程
8.4 当notify退出同步块之后,根据QMode的不同,将ObjectWaiter对象从_EntryList队列或_cxq中取出,在竞争锁
8.4.1
QMode=2,并且_cxq非空:取_cxq队列排头位置的ObjectWaiter对象
QMode=3,并且_cxq非空:把_cxq队列首元素放入到_EntryList队列的尾部
QMode=4,并且_cxq非空:取_cxq队列首元素放入到_EntryList队列的头部
参考:
https://cloud.tencent.com/developer/article/1013062
https://www.jianshu.com/p/f4454164c017
领取专属 10元无门槛券
私享最新 技术干货