摘要:condition用于显式的等待通知,等待过程可以挂起并释放锁,唤醒后重新拿到锁 。本文分享自华为云社区《AQS中的condition源码原理详细分析》,作者:breakDawn 。
condition的用法condition用于显式的等待通知,等待过程可以挂起并释放锁,唤醒后重新拿到锁 。
和直接用lock\unlock去做等待通知的区别在于,lock是不会释放锁的,但是利用的condition的await则可以,且唤醒后会自动重新拿回锁 。
Lock lock = new ReentrantLock();Condition condition = lock.newCondition();public void conditionWait() throws InterruptedException { lock.lock(); try { // if(xxxx)判断不满足条件,等待,释放锁 condition.await(); } finally { lock.unlock(); }}public void conditionSignal() throws InterruptedException { lock.lock(); try { // 做完事情了,通知condition上等待的开始抢占 condition.signal(); } finally { lock.unlock(); }}也提供了一些支持中断、支持超时的等待方法
condition 和 object.wait/notify的区别
- object的wait依赖sync,只能最多有一个等待队列 。而通过newCondition可以制造多个等待队列
- wait不支持中断,而condition支持
- condition支持等待特定时间
- await(),简单来讲就是把当前线程放入condition的等待队列中,然后调用LockSupport.park拉起线程 。如果被其他线程通过signal唤醒,则放入同步队列中竞争锁,竞争成功则返回,否则继续竞争 。
- signal方法,就是拿到condition的等待队列头节点,用cas修改节点状态,改成功则唤醒线程 。但有可能被别人抢先,所以需要cas操作 。
文章插图
代码结构部分:? Lock提供了newCondition接口给外部锁调用
? 而newCondition()返回的Condition是一个接口
文章插图
? 这个接口的实现类是ConditionObject,放在AQS抽象类的内部类中
文章插图
原理实现部分等待队列
- 每个condition都有一个属于自己的等待队列
- 每次调用condition.await,就插入到等待队列尾部
- 等待队列插入封装线程的节点时不需要在尾部CAS,因为必须先获取锁,才能调用await,因此不用CAS竞争
- 每个Lock只有一个同步队列(用于lock()时阻塞和竞争用),但是可能会有多个等待队列(用于condition的await)
- 添加线程到condition的等待队列尾部
- 释放占用的锁,并唤醒同步队列的后继节点
- 此时肯定不在aqs的同步队列中了,用park方法进入阻塞状态
- 被唤醒,唤醒时可能是通过sign()被人放入了同步队列,也可能是被中断唤醒,因此要做checkInterruptWhileWaiting检查看是否继续,如果同意继续,就继续睡眠,直到进入同步队列
- 尝试acquireQueued竞争和抢占state同步状态
- 退出前,顺带用unlinkCancelledWaiters清理已经不是CONDITION状态的等待队列节点
经验总结扩展阅读
- 朴东民是什么电影中的人物?
- 叶隐文正是什么电视剧中的人物?
- 最能洞察人心的星座隐藏在人群中的高手星座
- 哪些星座会因爱情失财
- 图数据 3D 可视化在 Explorer 中的应用
- .net 温故知新:【8】.NET 中的配置从xml转向json
- 2022兔年动土吉日查询
- 双燕和一凡是哪部电视剧中的人物?
- 厨师傻柱是什么电视剧中的人物?
- FHQ Treap 详解