关于await , 有几个关键点要说明:
- 线程调用await()的时候 , 肯定已经先拿到锁 。所以 , 在addConditionWaiter()内部 , 对这个双向链表的操作不需要执行cas操作 , 线程天生是安全的 , 代码如下:
private Node addConditionWaiter() {// ...//拿到末端的nodeNode t = lastWaiter;//new一个nodeNode node = new Node(Thread.currentThread(), Node.CONDITION);if (t == null)firstWaiter = node;elset.nextWaiter = node;lastWaiter = node;return node;}复制代码- 在线程执行wait操作之前 , 必须先释放锁 。也就是fullyRelease(node) , 否则会发生死锁 。这个和wait/notify与synchronized的配合机制一样
- 线程从wait中被唤醒后 , 必须用acquireQueue(node,savedState)方法重新拿锁 。
- checkInterruptWhileWaiting(node)代码在park(this)代码之后 , 是为了检测在park期间是否收到过中断信号 。当线程从park中醒来时 , 有两种可能:一种是其他线程调用了unpark , 另一种是收到中断信号 。这里的await()方法是可以响应中断的 , 所以当发现自己是被中断唤醒的 , 而不是被unpark唤醒的时候 , 会直接退出while循环 , await()方法也会返回 。
- isOnSyncQueue(node)用于判断该Node是否在AQS的同步队列里面 。初始的时候 , Node只在Condition的队列里 , 而不在AQS的队列里 。但执行notity操作的时候 , 会放进AQS的同步队列
public final void awaitUninterruptibly() {Node node = addConditionWaiter();int savedState = fullyRelease(node);boolean interrupted = false;while (!isOnSyncQueue(node)) {LockSupport.park(this);if (Thread.interrupted())interrupted = true;}if (acquireQueued(node, savedState) || interrupted)selfInterrupt();}复制代码可以看出 , 整体代码和await()类似 , 区别在于收到异常后 , 不会抛出异常 , 而是继续执行while循环Condition.signal
调用Condition的signal方法 , 将会唤醒在等待队列中等待时间最长的节点(首节点) , 在唤醒节点之前 , 会将节点移到同步队列中
public final void signal() {if (!isHeldExclusively())//先判断当前线程是否获取了锁throw new IllegalMonitorStateException();Node first = firstWaiter;//拿到Condition队列上第一个节点if (first != null)doSignal(first);}复制代码Condition.doSignalprivate void doSignal(Node first) {do {if ( (firstWaiter = first.nextWaiter) == null)//如果第一个节点的下一个节点是null , 那么 , 最后一个节点也是nulllastWaiter = null;//将next节点设置成nullfirst.nextWaiter = null;} while (!transferForSignal(first) &&(first = firstWaiter) != null);}复制代码【还不知道ReentrantLock的实现流程,那你就out了】final boolean transferForSignal(Node node) {Node p = enq(node);int ws = p.waitStatus;//如果上一个节点的状态被取消了 , 或者尝试设置上一个节点的状态为SIGNAL失败了(SIGNAL 表示: 他的 next 节点需要停止阻塞)if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))LockSupport.unpark(node.thread);//唤醒输入节点上的线程return true;}复制代码该方法先是CAS修改了节点状态 , 如果成功 , 就将这个节点放到AQS队列中 , 然后唤醒这个节点上的线程 。此时 , 那个节点就会在await方法中苏醒Condition总结阻塞:await()方法中 , 在线程释放锁资源之后 , 如果节点不在 AQS 等待队 列 , 则阻塞当前线程 , 如果在等待队列 , 则自旋等待尝试获取锁
释放:signal()后 , 节点会从 condition 队列移动到 AQS 等待队列 , 则进入 正常锁的获取流程
作者:Five在努力
链接:https://juejin.cn/post/6918557057947795470
推荐阅读
- 龙井属于红茶还是绿茶,西湖龙井和金骏眉哪个好
- iphonex和iphone8plus哪个好-iphone8p还是x-
- 招聘|大学生找工作时,到底是校招有用,还是网络招聘有用?
- 八爪鱼是鱿鱼吗
- 饵料|钓鱼还真是个技术活,盘点后会吃惊,钓鱼人的自豪感油然而生
- 白茶散茶和茶饼的区别,白茶饼是泡还是煮
- news是单数吗?news用单数还是复数
- 绿茶时间长了能喝吗,菊花茶变成绿色之后还能喝吗
- 枕头高点好还是低点好?
- miniled好还是oled好-oled和mini led哪个好-
