ReentrantReadWriteLock源碼分析

在看本文前,如果對ReentrantLock不瞭解的,請先看 ReentrantLock源碼分析,以免有些地方無法理解。
代碼的執行說明,都已註釋的方式寫在了代碼中,如果想看某個方法的分析,直接搜索方法名即可。本文中對於在ReentrantLock中詳細說明過的方法以及相關分析在這裏沒有詳細說明,請直接看ReentrantLock源碼分析

ReadLock

lock

 public final void acquireShared(int arg) {
 	// 通關acquireShared嘗試去獲取共享鎖
 	//獲取成功了。就什麼都不做。獲取失敗了,則執行doAcquireShared將當前線程加入到同步隊列中
     if (tryAcquireShared(arg) < 0)
         doAcquireShared(arg);
 }

在看tryAcquireShared之前,需要先理解對於讀寫鎖,是如何保存鎖狀態的?AQS是通過state來記錄所得狀態,而state只是一個值,對於讀寫鎖來說,怎麼用state來同時記錄讀和寫兩種鎖的狀態的呢?很容易想到的就是將state分爲兩部分,而ReentrantReadWriteLock也是這麼做的,他使用低16位表示寫鎖狀態,高16位用來記錄讀鎖狀態。具體如下:

 static final int SHARED_SHIFT   = 16;
 static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
 static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
 static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
 /** Returns the number of shared holds represented in count  */
 //取state的高16位,共享鎖(讀鎖)
 static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
 /** Returns the number of exclusive holds represented in count  */
 //取state的低16位,獨佔鎖(寫鎖)
 static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
protected final int tryAcquireShared(int unused) {
    /*
     * Walkthrough:
     * 1. If write lock held by another thread, fail.
     * 2. Otherwise, this thread is eligible for
     *    lock wrt state, so ask if it should block
     *    because of queue policy. If not, try
     *    to grant by CASing state and updating count.
     *    Note that step does not check for reentrant
     *    acquires, which is postponed to full version
     *    to avoid having to check hold count in
     *    the more typical non-reentrant case.
     * 3. If step 2 fails either because thread
     *    apparently not eligible or CAS fails or count
     *    saturated, chain to version with full retry loop.
     */
    Thread current = Thread.currentThread();
    //獲取state
    int c = getState();
    //獲取獨佔鎖,如果獨佔鎖不爲0,說明現在已經有線程獲取了獨佔鎖。再判斷不是線程重入。
    //此時直接返回-1,表示獲取鎖失敗了。因爲獨佔鎖與獨佔鎖,獨佔鎖與共享鎖都是互斥關係。
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    //執行到這兒,說明獨佔鎖狀態爲0.
    //獲取共享鎖的狀態
    int r = sharedCount(c);
    //readerShouldBlock 判斷的是讀需不需要被阻塞
    //(實現後面會分析):在非公平鎖中,實現判斷就是說,當阻塞隊列中的第一個等待線程爲獨佔鎖,返回true。
    //之所以這樣判斷,是因爲,如果當前有線程持有共享鎖,然後繼續有持續不斷的線程來獲取共享鎖,就會導致搶佔獨佔鎖的線程長時間無法獲取鎖而飢餓。
    if (!readerShouldBlock() &&
        r < MAX_COUNT &&
        //重新設置state值來獲取共享鎖,可能有其他線程先修改了state,那這裏就會失敗,就會執行fullTryAcquireShared
        compareAndSetState(c, c + SHARED_UNIT)) {
        //執行到這兒,說明獲取共享鎖成功了。
        //這裏判斷r如果等於0說明當前線程是第一個獲取共享鎖的線程。
        if (r == 0) {
        	//firstReader用於保存第一個獲得讀鎖的線程
        	//firstReaderHoldCount 用於保存第一個獲取共享鎖的線程的重入次數。
        	//這樣做主要是爲了當只有一個線程訪問鎖,沒有競爭的時候,將鎖的狀態信息放在 firstReader和firstReaderHoldCount中,效率更高,因爲後面你會發現要將當前線程的重入次數放在的ThreadLocal中。
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
        	//如果是第一個線程重入,則直接將重入次數++
            firstReaderHoldCount++;
        } else {
        	//cachedHoldCounter是一個HoldCounter,用來保存一個線程的重入次數的,這個變量正如其名,是一個緩存。
        	//因爲要保存每一個線程的重入次數,這裏將其保存在了ThreadLocal中,
        	//如果沒有這個緩存,每次重入次數並修改,都要從ThreadLocal中獲取,這裏使用一層cache,主要就是爲了提升性能,
        	//只有cache中的線程不是當前線程的時候,纔會從ThreadLocal中獲取。
            HoldCounter rh = cachedHoldCounter;
            if (rh == null || rh.tid != getThreadId(current))
            	//cache中的線程不是當前線程的時候,從ThreadLocal中獲取。
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
          		//這裏進行了set,這個set,有一種情況就是,一個線程釋放了鎖,釋放的時候會將HoldCounter 從當前線程中remove,但是此時cachedHoldCounter還繼續緩存着這個線程。
          		//如果剛釋放又重新要獲取鎖,此時可能cachedHoldCounter又剛好是當前線程,那麼這個時候ThreadLocal中已經沒有數據了,
          		//此時就需要重新初始化ThreadLocal中的HoldCounter,重新設置值。
                readHolds.set(rh);
            //重入次數++,如果是第一次,這裏++之後的結果就是1
            rh.count++;
        }
        return 1;
    }
    //如果不滿足前面三個條件的任何一個條件,都會執行fullTryAcquireShared
    return fullTryAcquireShared(current);
}
//這裏的邏輯其實就是獲取鎖,和tryAcquireShared中的很多邏輯是一樣的,相同的地方,這裏不再詳細備註
//只是這裏如果失敗則通過死循環不斷嘗試知道滿足條件返回。
final int fullTryAcquireShared(Thread current) {
    /*
     * This code is in part redundant with that in
     * tryAcquireShared but is simpler overall by not
     * complicating tryAcquireShared with interactions between
     * retries and lazily reading hold counts.
     */
    HoldCounter rh = null;
    for (;;) {
        int c = getState();
        if (exclusiveCount(c) != 0) {
            if (getExclusiveOwnerThread() != current)
                return -1;
            // else we hold the exclusive lock; blocking here
            // would cause deadlock.
        } else if (readerShouldBlock()) {
            // Make sure we're not acquiring read lock reentrantly
            //這裏即使readerShouldBlock未true(說明等待隊列中第一個節點是在搶佔獨佔鎖),但是可能是線程重入的情況
            //這裏要判斷,如果是重入則放過(重入次數++的邏輯,在後面做的),如果不是重入,則返回-1
            //判斷不是重入的邏輯:首先線程不能等於firstReader ,其次其重入次數不等於0,因爲等於0說明是第一次進入才初始化ThreadLocal中的HoldCounter。
           
           //這裏判斷等於firstReader ,說明是重入則放過
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
            } else {
            	//拿到當前線程的HoldCounter
                if (rh == null) {
                    rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current)) {
                        rh = readHolds.get();
                        //如果重入次數爲0,說明是第一次進入,因爲要readerShouldBlock爲true,所以要將其掛起,所以直接將其再刪除掉。
                        if (rh.count == 0)
                            readHolds.remove();
                    }
                }
                //如果這是一個新的來競爭的線程,並且是第一次來獲取鎖,此時rh.count == 0,那就需要將當前線程掛起。
                //所以這裏返回-1,說明獲取鎖失敗了,要將其掛起。
                if (rh.count == 0)
                    return -1;
            }
        }
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        if (compareAndSetState(c, c + SHARED_UNIT)) {
        	//如果這裏修改state的值成功了,說明獲取共享鎖成功,下面要做的就是通過ThreadLocak記錄重入次數了。
        	//如果這裏修改失敗了,說明已經有其他線程修改了state,那就會進入下一次循環,再次來獲取鎖。
            if (sharedCount(c) == 0) {
                firstReader = current;
                firstReaderHoldCount = 1;
            } else if (firstReader == current) {
                firstReaderHoldCount++;
            } else {
                if (rh == null)
                    rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                else if (rh.count == 0)
                    readHolds.set(rh);
                rh.count++;
                cachedHoldCounter = rh; // cache for release
            }
            return 1;
        }
    }
}
//如果獲取共享鎖失敗了,則通過此方法,將線程加入到同步隊列並掛起。
private void doAcquireShared(int arg) {
	//將當前線程封裝成Node節點並加入到同步隊列中
    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做了兩件事。
                	//一是重新設置head
                	//二是喚醒下一個節點
                    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);
    }
}
private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // Record old head for check below
    //重新設置head
    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();
    }
}

unlock

public final boolean releaseShared(int arg) {
	//釋放共享鎖
    if (tryReleaseShared(arg)) {
    	//喚醒節點線程
        doReleaseShared();
        return true;
    }
    return false;
}
protected final boolean tryReleaseShared(int unused) {
    Thread current = Thread.currentThread();
    //減少重入次數的操作
    if (firstReader == current) {
        // assert firstReaderHoldCount > 0;
        if (firstReaderHoldCount == 1)
            firstReader = null;
        else
            firstReaderHoldCount--;
    } else {
        HoldCounter rh = cachedHoldCounter;
        if (rh == null || rh.tid != getThreadId(current))
            rh = readHolds.get();
        int count = rh.count;
        if (count <= 1) {
            readHolds.remove();
            if (count <= 0)
                throw unmatchedUnlockException();
        }
        --rh.count;
    }
    for (;;) {
    	//修改鎖標誌state,因爲共享鎖釋放存在多線程競爭的情況,所以這裏使用CAS+循環直到釋放成功
    	//只有state爲0,才返回true說明已經全部釋放。
        int c = getState();
        int nextc = c - SHARED_UNIT;
        if (compareAndSetState(c, nextc))
            // Releasing the read lock has no effect on readers,
            // but it may allow waiting writers to proceed if
            // both read and write locks are now free.
            return nextc == 0;
    }
}
private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                	//可能有多個線程同進行喚醒操作,這個CAS保證一個節點只會被unpark喚醒一次。
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                //執行到這兒,說明可能有其他線程已經執行了喚醒操作,ws已經被修改爲0
                //在此通過CAS將0替換成PROPAGATE,此時如果替換成功,那說明確實已經被其他線程喚醒成功。則直接會執行h == head判斷並跳出
                //如果CAS替換失敗了,其他線程已經將其替換爲了PROPAGATE。則下一次循環就行執行h == head判斷並跳出
                //如果此時waitstatus不等於0,說明waitstatus等於PROPAGATE,說明第一個節點已經被其他線程喚醒,並且此時head還沒有改變。
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            //判斷head如果確實沒有改變,則說明第一個節點已經被正常喚醒。
            if (h == head)                   // loop if head changed
                break;
        }
    }

WriteLock

lock

//WriteLock是獨佔鎖,其獲取鎖的過程和ReentrantLock是相似的,區別在於tryAcquire的實現
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
    /*
     * Walkthrough:
     * 1. If read count nonzero or write count nonzero
     *    and owner is a different thread, fail.
     * 2. If count would saturate, fail. (This can only
     *    happen if count is already nonzero.)
     * 3. Otherwise, this thread is eligible for lock if
     *    it is either a reentrant acquire or
     *    queue policy allows it. If so, update state
     *    and set owner.
     */
    Thread current = Thread.currentThread();
    int c = getState();
    //拿到獨佔鎖標誌位
    int w = exclusiveCount(c);
    //c != 0說明w肯定也是非0的。c == 0說明w == 0
    if (c != 0) {
        // (Note: if c != 0 and w == 0 then shared count != 0)
       //有線程已經佔用了獨佔鎖,並且不是鎖重入,直接返回false
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // Reentrant acquire
        //鎖重入則修改state的值
        setState(c + acquires);
        return true;
    }
    //writerShouldBlock判斷是否應該被阻塞
    //對於非公平鎖,允許後來的線程搶佔鎖,就返回false
    //對於公平鎖,如果等待隊列有等待線程,則不允許插隊,返回false
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}

unlock

//實現和ReentrantLock的unlock也是類似的,區別在於release的實現
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
//其實很簡單,就是對state的值做減法操作,等於0則說明鎖全部被釋放。
protected final boolean tryRelease(int releases) {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    int nextc = getState() - releases;
    boolean free = exclusiveCount(nextc) == 0;
    if (free)
        setExclusiveOwnerThread(null);
    setState(nextc);
    return free;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章