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

概述

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

acquireShared

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

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

doAcquireShared

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

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或者为propagatethis

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();
        }
    }
相关文章
相关标签/搜索