Java读源码之ReentrantLock(2) (3)

AbstractQueuedSynchronizer.ConditionObject#unlinkCancelledWaiters

private void unlinkCancelledWaiters() { Node t = firstWaiter; // 辅助变量,用于接尾巴,trail始终等于循环中当前节点t的上一个不是取消状态的节点 Node trail = null; while (t != null) { Node next = t.nextWaiter; // 判断当前节点有没有取消 if (t.waitStatus != Node.CONDITION) { // 断当前节点链 t.nextWaiter = null; // trail == null 说明目前条件队列里面全取消了 if (trail == null) // 头节点指向第一个没取消的节点 firstWaiter = next; else // trail 是 t 的前一个节点,也就是踢出了 t trail.nextWaiter = next; // 如果最后一个节点取消了,那需要改一下尾指针 if (next == null) lastWaiter = trail; } else trail = t; t = next; } }

AbstractQueuedSynchronizer.ConditionObject#checkInterruptWhileWaiting

上文 await 方法中,线程一旦唤醒会先检查中断

private int checkInterruptWhileWaiting(Node node) { // 没中断,返回0,中断了需要放回同步队列 return Thread.interrupted() ? (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) : 0; }

AbstractQueuedSynchronizer#transferAfterCancelledWait

// 如果 final boolean transferAfterCancelledWait(Node node) { // 把因为中断醒来的节点,设置状态为全新的节点,从条件队列放入同步队列 if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) { enq(node); return true; } // 上面改状态为什么要 CAS ? 如果中断唤醒的同时被 signal 唤醒了,在 signal 入队成功之前让出cpu,但是不释放锁 while (!isOnSyncQueue(node)) Thread.yield(); return false; } 条件队列出队

单个唤醒和唤醒所以掉的方法类似,看一个单个唤醒流程就可

AbstractQueuedSynchronizer.ConditionObject#signal

public final void signal() { // 如果持有锁的线程不是当前线程就抛异常,也就是只有获得锁的线程可以执行唤醒操作 if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; // 通知条件队列中的第一个节点,也就是等的最久的节点 if (first != null) doSignal(first); }

AbstractQueuedSynchronizer.ConditionObject#doSignal

private void doSignal(Node first) { do { // 把 first 断链 if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; first.nextWaiter = null; // 如果转移到同步队列失败了,并且还有条件队列不为空就唤醒下一个 } while (!transferForSignal(first) && (first = firstWaiter) != null); }

AbstractQueuedSynchronizer#transferForSignal

final boolean transferForSignal(Node node) { // 如果节点取消了,转移失败 if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; // 这里的 p 是 node 在同步队列里的前驱节点 Node p = enq(node); int ws = p.waitStatus; // 看过上一篇文章应该有映像,只要是进同步队列,都需要把前一个节点状态设为 -1 if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) // 如果取消了,或者状态设置失败,唤醒后继续挂起 LockSupport.unpark(node.thread); return true; }

最后按照惯例结合上面的案例,画张图总结下:

Java读源码之ReentrantLock(2)

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

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