硬核剖析Java锁底层AQS源码,深入理解底层架构设计( 三 )

再看一下唤醒后继节点的方法
// 唤醒后继节点private void unparkSuccessor(Node node) {int ws = node.waitStatus;// 1. 如果头节点不是取消状态 , 就重置成初始状态if (ws < 0)compareAndSetWaitStatus(node, ws, 0);Node s = node.next;// 2. 如果后继节点是null或者是取消状态if (s == null || s.waitStatus > 0) {s = null;// 3. 从队尾开始遍历,找到一个有效状态的节点for (Node t = tail; t != null && t != node; t = t.prev)if (t.waitStatus <= 0)s = t;}// 3. 唤醒这个有效节点if (s != null)LockSupport.unpark(s.thread);}4.3 await等待await等待的流程:

硬核剖析Java锁底层AQS源码,深入理解底层架构设计

文章插图
持有锁的线程可以调用await方法,作用是:释放锁,并追加到条件队列末尾 。
// 等待方法public final void await() throws InterruptedException {// 如果线程已中断 , 则中断if (Thread.interrupted())throw new InterruptedException();// 1. 追加到条件队列末尾Node node = addConditionWaiter();// 2. 释放锁int savedState = fullyRelease(node);int interruptMode = 0;// 3. 有可能刚加入条件队列就被转移到同步队列了,如果还在条件队列,就可以放心地挂起自己while (!isOnSyncQueue(node)) {LockSupport.park(this);if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)break;}// 4. 如果已经转移到同步队列,就尝试获取锁if (acquireQueued(node, savedState) && interruptMode != THROW_IE)interruptMode = REINTERRUPT;if (node.nextWaiter != null)// 5. 清除条件队列中已取消的节点unlinkCancelledWaiters();if (interruptMode != 0)reportInterruptAfterWait(interruptMode);}再看一下addConditionWaiter方法,是怎么追加到条件队列末尾的?
// 追加到条件队列末尾private Node addConditionWaiter() {Node t = lastWaiter;// 1. 清除已取消的节点,找到有效节点if (t != null && t.waitStatus != Node.CONDITION) {unlinkCancelledWaiters();t = lastWaiter;}// 2. 创建Node节点 , 状态是-2(表示处于条件队列)Node node = new Node(Thread.currentThread(), Node.CONDITION);// 3. 追加到条件队列末尾if (t == null)firstWaiter = node;elset.nextWaiter = node;lastWaiter = node;return node;}4.4 signal唤醒signal唤醒的流程:
硬核剖析Java锁底层AQS源码,深入理解底层架构设计

文章插图
唤醒条件队列的头节点 , 并追加到同步队列末尾 。
// 唤醒条件队列的头节点public final void signal() {// 1. 只有持有锁的线程才能调用signal方法if (!isHeldExclusively())throw new IllegalMonitorStateException();// 2. 找到条件队列的头节点Node first = firstWaiter;if (first != null)// 3. 开始唤醒doSignal(first);}// 实际的唤醒方法private void doSignal(Node first) {do {// 4. 从条件队列中移除头节点if ((firstWaiter = first.nextWaiter) == null)lastWaiter = null;first.nextWaiter = null;// 5. 使用死循环,一定要转移一个节点到同步队列} while (!transferForSignal(first) &&(first = firstWaiter) != null);}到底是怎么转移到同步队列末尾的?
// 实际转移方法final boolean transferForSignal(Node node) {// 1. 把节点状态从CONDITION改成0if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))return false;// 2. 使用死循环的方式,追加到同步队列末尾(前面已经讲过)Node p = enq(node);int ws = p.waitStatus;// 3. 把前驱节点状态设置SIGNAL(通知他 , 别忘了唤醒老弟)if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))LockSupport.unpark(node.thread);return true;}5. 总结看完整个AQS的源码,是不是完全理解了AQS加锁、释放锁、以及同步队列和条件队列数据流转的逻辑了 。
硬核剖析Java锁底层AQS源码,深入理解底层架构设计

文章插图
连AQS这么复杂的源码你都搞清楚了,下篇带你一块学习ReentrantLock源码 , 应该就轻松多了 。
我是「一灯架构」,如果本文对你有帮助,欢迎各位小伙伴点赞、评论和关注,感谢各位老铁,我们下期见

硬核剖析Java锁底层AQS源码,深入理解底层架构设计

文章插图

推荐阅读