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();
        }
    }
相關文章
相關標籤/搜索