AQS共享模式

    首先来回顾一下AQS Node的主要结构,对于节点状态PROPAGATE的无条件传播,可能不理解。看完doAcquireShared的源码就会知道了

 static final class Node {
        // 标识节点正在共享模式下
        static final Node SHARED = new Node();
        // 标识节点正在独占模式下
        static final Node EXCLUSIVE = null;
        // 标识该节点的动作被取消
        static final int CANCELLED =  1;
        // 标识后续节点需要被唤醒
        static final int SIGNAL    = -1;
        // 标识节点正在等待某些条件(ConditionObject)
         static final int CONDITION = -2;
        // 标识下一次的acquireShared,应该无条件的传播(唤醒共享节点)
        static final int PROPAGATE = -3;
}
共享模式获取锁
public final void acquireShared(int arg) {
     // 获取锁失败,加入同步队列,并再次获取锁
     // tryAcquireShared需要实现类重写该方法
     if (tryAcquireShared(arg) < 0)
         doAcquireShared(arg);
 }

    tryAcquireShared的返回值,负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。

 private void doAcquireShared(int arg) {
        // 加入同步等待队列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                // 前置节点如果是头节点的话,那么再次尝试获取锁
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    // 获取锁成功
                    if (r >= 0) {
                        // 当前节点获取锁成功了,将该节点设置为头节点,并唤醒后继共享节点
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

为啥获取锁成功了,还要唤醒后继的共享节点?
    共享锁是可以多个线程共有的,当一个节点的线程获取共享锁后,必然要通知后继共享节点的线程,也可以获取锁了,这样就不会让其他等待的线程等很久,而传播性的目的也是尽快通知其他等待的线程尽快获取锁。

共享模式释放锁
 public final boolean releaseShared(int arg) {
        // tryReleaseShared需要子类实现
        if (tryReleaseShared(arg)) {
            // 释放锁成功后唤醒后继节点
            doReleaseShared();
            return true;
        }
        return false;
    }
  private void doReleaseShared() {
    
        for (;;) {
            Node h = head; // 暂存一下head节点到h
            // 如果有头节点,并且队列中不止头节点一个节点
            if (h != null && h != tail) {
                // 头节点的waitStatus是-1,唤醒后继节点,并且把状态更新成0
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
              // 节点的waitStatus已经是0了,说明已经唤醒过后续节点了,那么把他更新成PROPAGATE,下一次无条件传播
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            // 如果头节点没有变化,那么退出
            if (h == head)                   // loop if head changed
                break;
        }
    }

    从这里的逻辑可以看出,如果节点的waitStatus是-3,也就是PROPAGATE,会直接进入到下次循环重新拿头节点的状态,唤醒后续共享节点。这也就是我理解的无条件传播

你可能感兴趣的:(AQS共享模式)