图解AQS的设计与实现,手摸手带你实现一把互斥锁! (2)

图解AQS的设计与实现,手摸手带你实现一把互斥锁!

// 伪代码 final boolean acquireQueued(final Node node, int arg) { for (;;) { // -------获取同步状态失败------- // 获取失败后是否进入wait if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } /** * 当获取同步状态失败后是否进入park状态 */ private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; // 前驱节点为唤醒状态,返回true【后面代码暂时可以忽略】 if (ws == Node.SIGNAL) return true; if (ws > 0) { do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }

独占模式获取同步状态总结

图解AQS的设计与实现,手摸手带你实现一把互斥锁!

2.2 释放同步状态

当线程执行完相应逻辑后,需要释放同步状态,使后继节点有机会同步状态(让出资源,让排队的线程使用)。这时就需要调用release(int arg)方法。调用该方法后,会唤醒后继节点。

释放同步状态,唤醒后继节点

图解AQS的设计与实现,手摸手带你实现一把互斥锁!

/** * 释放同步状态 */ public final boolean release(int arg) { // 1. 尝试释放同步状态 if (tryRelease(arg)) { Node h = head; // 释放成功后,执行unpark,既唤醒操作(暂时可忽略waitStatus,涉及到条件队列) if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } /** * 尝试释放同步状态,既将同步状态减去指定的值 * 如果state = 0,表示当前线程 获取次数 = 释放次数,既释放成功,此时将持有同步状态线程标志为null */ protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; // 状态码=0,表示释放成功了 if (c == 0) { free = true; // 独占标志设置为null setExclusiveOwnerThread(null); } setState(c); return free; } /** * 唤醒后继节点操作 */ private void unparkSuccessor(Node node) { int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); // 获取后继节点 Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } // 唤醒后继节点 if (s != null) LockSupport.unpark(s.thread); }

后继节点获取同步状态成功,头节点出队。需要注意的事,出队操作是间接的,有节点获取到同步状态时,会将当前节点设置为head,而原本的head设置为null。

图解AQS的设计与实现,手摸手带你实现一把互斥锁!

/** * 同步队列中节点,尝试获取同步状态(伪代码) * 获取成功后,当前节点设置为头节点,头节点设置为null,既头节点出队 */ final boolean acquireQueued(final Node node, int arg) { try { // 自旋(死循环) for (;;) { if (p == head && tryAcquire(arg)) { // a. 操作:当前节点设置为头节点,当前节点的前驱节点设置为null setHead(node); // b. 原始的head的next设置为null,此时原始的head已经被移出队列 p.next = null; // help GC failed = false; return interrupted; } } } } /** * a.当前节点设置为头节点,当前节点的前驱节点设置为null */ private void setHead(Node node) { head = node; node.thread = null; node.prev = null; } 2.3 其他竞争情况

当同步队列中头节点唤醒后继节点时,此时可能有其他线程尝试获取同步状态。

图解AQS的设计与实现,手摸手带你实现一把互斥锁!

假设获取成功,将会被设置为头节点。

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

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