并发条件队列之Condition 精讲 (7)

只唤醒一个节点

public final void signal() { // getExclusiveOwnerThread() == Thread.currentThread(); 当前线 // 程是不是独占线程 if (!isHeldExclusively()) throw new IllegalMonitorStateException(); // 获取第一个阻塞线程节点 Node first = firstWaiter; // 条件队列是否为空 if (first != null) doSignal(first); } // 遍历整个条件队列,找到第一个没有被cancelled的节点,并将它添加到条件队列的末尾 // 如果条件队列里面已经没有节点了,则将条件队列清空 private void doSignal(Node first) { do { // 将firstWaiter指向条件队列队头的下一个节点 if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; // 将条件队列原来的队头从条件队列中断开,则此时该节点成为一个孤立的节点 first.nextWaiter = null; } while (!transferForSignal(first) && (first = firstWaiter) != null); }

方法总结:
       调用signal()方法会从当前条件队列中取出第一个没有被cancel的节点添加到sync队列的末尾。

7. signalAll

唤醒所有的节点

public final void signalAll() { // getExclusiveOwnerThread() == Thread.currentThread(); 当前线 // 程是不是独占线程 if (!isHeldExclusively()) throw new IllegalMonitorStateException(); // 获取第一个阻塞线程节点 Node first = firstWaiter; // 条件队列是否为空 if (first != null) doSignalAll(first); } // 移除并转移所有节点 private void doSignalAll(Node first) { // 清空队列中所有数据 lastWaiter = firstWaiter = null; do { Node next = first.nextWaiter; first.nextWaiter = null; transferForSignal(first); first = next; } while (first != null); } // 将条件队列中的节点一个一个的遍历到同步队列中 final boolean transferForSignal(Node node) { // 如果该节点在调用signal方法前已经被取消了,则直接跳过这个节点 if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; // 利用enq方法将该节点添加至同步队列的尾部 Node p = enq(node); // 返回的是前驱节点,将其设置SIGNAL之后,才会挂起 // 当前节点 int ws = p.waitStatus; if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) LockSupport.unpark(node.thread); return true; }

       在transferForSignal方法中,我们先使用CAS操作将当前节点的waitStatus状态由CONDTION设为0,如果修改不成功,则说明该节点已经被CANCEL了则我们直接返回操作下一个节点;如果修改成功,则说明我们已经将该节点从等待的条件队列中成功“唤醒”了,但此时该节点对应的线程并没有真正被唤醒,它还要和其他普通线程一样去争锁,因此它将被添加到同步队列的末尾等待获取锁 。
方法总结:

将条件队列清空(只是令lastWaiter = firstWaiter = null,队列中的节点和连接关系仍然还存在)

将条件队列中的头节点取出,使之成为孤立节点(nextWaiter,prev,next属性都为null)

如果该节点处于被Cancelled了的状态,则直接跳过该节点(由于是孤立节点,则会被GC回收)

如果该节点处于正常状态,则通过enq方法将它添加到同步队列的末尾

判断是否需要将该节点唤醒(包括设置该节点的前驱节点的状态为SIGNAL),如有必要,直接唤醒该节点

重复2-5,直到整个条件队列中的节点都被处理完

6. 总结

       以上便是Condition的分析,下一篇文章将是并发容器类的分析,如有错误之处,帮忙指出及时更正,谢谢, 如果喜欢谢谢点赞加收藏加转发(转发注明出处谢谢!!!)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wssfjf.html