AbstractQueuedSynchronizer那些事儿(四) 共享模式下的acquireShared

概述

上文分析了独占模式下的acquire实现,本章分析一下共享模式下的acquireShared实现

acquireShared

每个线程都会尝试去获取共享锁,只有获取失败的才会进入doAcquireShared方法

  public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

doAcquireShared

这里共享锁与独占锁的实现类似
首先,也是判断当前节点是否是head节点的有效后继节点,如果是的话,当前节点就回去尝试获取一次共享锁,如果获取成功就调用setHeadAndPropagate继续传播,如果不是head节点的有效后继节点,就判断当前节点是否应该阻塞,剩下的逻辑与acquire类似就不再分析,重点关注一下setHeadAndPropagate的实现

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);
        }

setHeadAndPropagate

设置head节点的同时根据propagate值做逻辑处理,注意进入此方法前,一定是由于有线程调用了doReleaseShared方法,而在该方法执行后的head的waitStatus状态只能为0或者为propagate

1.h=null 和 (h=head)=null只是为了防止null指针异常,一般情况下是不会出现这俩种情况的,重点关注别的判断
如果propagate>0说明还有共享锁可以获取,那么直接读取当前节点的后继节点,如果后继节点为null或者是共享模式才调用doReleaseShared方法,前面的章节分析中我们知道这个方法就是在一定的条件下唤醒head的后继节点,
2.如果propagate=0 且 旧节点的waitStatus<0,根据上面的分析可知此时head的waitStatus为propagate,同时,propagate=0也只是说明线程执行到这里时没有锁可获取,但是在之后有另一个线程释放了锁,导致head的waitStatus<0,所以也需要调用doReleaseShared来唤醒head的后继节点继续来尝试获取锁
3.如果propagate=0 且 旧head节点的waitStatus=0 且新的head节点的waitStatus<0,旧的head节点为0,很正常,因为唤醒该新节点时本就会把旧的head状态设为0,而新节点被设置为head后,之前本就处于SIGNAL状态来阻塞它的后继节点,它的状态自然小于0,因此也就造成了不必要的唤醒
 private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        /*
         * Try to signal next queued node if:
         *   Propagation was indicated by caller,
         *     or was recorded (as h.waitStatus either before
         *     or after setHead) by a previous operation
         *     (note: this uses sign-check of waitStatus because
         *      PROPAGATE status may transition to SIGNAL.)
         * and
         *   The next node is waiting in shared mode,
         *     or we don't know, because it appears null
         *
         * The conservatism in both of these checks may cause
         * unnecessary wake-ups, but only when there are multiple
         * racing acquires/releases, so most need signals now or soon
         * anyway.
         */
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

你可能感兴趣的:(java,并发)