一起讀源碼(二) ReadWriteLock源碼ReadLock解析

Java併發包中ReadWriteLock是一個接口,主要有兩個方法

public interface ReadWriteLock {
    /**
     * 返回讀鎖
     */
    Lock readLock();

    /**
     * 返回寫鎖
     */
    Lock writeLock();
}

Java併發庫中ReetrantReadWriteLock實現了ReadWriteLock接口,並添加了可重入的特性

構造方法

ReentrantReadWriteLock有兩個構造方法

    public ReentrantReadWriteLock() {
        this(false);
    }

    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

默認的構造方法使用的是非公平模式,創建的Sync是NonfairSync對象,然後初始化讀鎖和寫鎖。

同步器

FairSync的實現:

	static final class FairSync extends Sync {
        private static final long serialVersionUID = -2274990926593161451L;
        final boolean writerShouldBlock() {
            return hasQueuedPredecessors();
        }
        final boolean readerShouldBlock() {
            return hasQueuedPredecessors();
        }
    }

再來看NonfairSync的實現:

static final class NonfairSync extends Sync {
       private static final long serialVersionUID = -8159625535654395037L;
       final boolean writerShouldBlock() {
           return false; // writers can always barge
       }
       final boolean readerShouldBlock() {
           /* As a heuristic to avoid indefinite writer starvation,
            * block if the thread that momentarily appears to be head
            * of queue, if one exists, is a waiting writer.  This is
            * only a probabilistic effect since a new reader will not
            * block if there is a waiting writer behind other enabled
            * readers that have not yet drained from the queue.
            */
           return apparentlyFirstQueuedIsExclusive();
       }
   }

非公平模式下,writerShouldBlock直接返回false,說明不需要阻塞;而readShouldBlock調用了apparentFirstQueuedIsExcluisve()方法。
繼承AQS的類都需要使用state變量代表某種資源,ReentrantReadWriteLock中的state代表了讀鎖的數量和寫鎖的持有與否,整個結構如下:
在這裏插入圖片描述
state的高16位代表讀鎖的個數,低16位代表寫鎖的狀態

獲取鎖

讀鎖的獲取
當需要使用讀鎖時,首先調用lock方法

	public void lock() {
        sync.acquireShared(1);
    }

AQS的acquireShared方法

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

當tryAcquireShared()方法小於0時,那麼會執行doAcquireShared方法將該線程加入到等待隊列中。

	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();
	    int c = getState();
	    //如果當前有寫線程並且本線程不是寫線程,不符合重入,失敗
	    if (exclusiveCount(c) != 0 &&
	        getExclusiveOwnerThread() != current)
	        return -1;
	    //得到讀鎖的個數
	    int r = sharedCount(c);
	    //如果讀不應該阻塞並且讀鎖的個數小於最大值65535,並且可以成功更新狀態值,成功
	    if (!readerShouldBlock() &&
	        r < MAX_COUNT &&
	        compareAndSetState(c, c + SHARED_UNIT)) {
	        //如果當前讀鎖爲0
	        if (r == 0) {
	            //第一個讀線程就是當前線程
	            firstReader = current;
	            firstReaderHoldCount = 1;
	        }
	        //如果當前線程重入了,記錄firstReaderHoldCount
	        else if (firstReader == current) {
	            firstReaderHoldCount++;
	        }
	        //當前讀線程和第一個讀線程不同,記錄每一個線程讀的次數
	        else {
	            HoldCounter rh = cachedHoldCounter;
	            if (rh == null || rh.tid != getThreadId(current))
	                cachedHoldCounter = rh = readHolds.get();
	            else if (rh.count == 0)
	                readHolds.set(rh);
	            rh.count++;
	        }
	        return 1;
	    }
	    //循環嘗試
	    return fullTryAcquireShared(current);
	}

fullTryAcquiredShared方法

final int fullTryAcquireShared(Thread current) {
        HoldCounter rh = null;
        for (;;) {
            int c = getState();
            //一旦有別的線程獲得了寫鎖,返回-1,失敗
            if (exclusiveCount(c) != 0) {
                if (getExclusiveOwnerThread() != current)
                    return -1;
            } 
            //如果讀線程需要阻塞
            else if (readerShouldBlock()) {
                // Make sure we're not acquiring read lock reentrantly
                if (firstReader == current) {
                    // assert firstReaderHoldCount > 0;
                }
                //說明有別的讀線程佔有了鎖
                else {
                    if (rh == null) {
                        rh = cachedHoldCounter;
                        if (rh == null || rh.tid != getThreadId(current)) {
                            rh = readHolds.get();
                            if (rh.count == 0)
                                readHolds.remove();
                        }
                    }
                    if (rh.count == 0)
                        return -1;
                }
            }
            //如果讀鎖達到了最大值,拋出異常
            if (sharedCount(c) == MAX_COUNT)
                throw new Error("Maximum lock count exceeded");
            //如果成功更改狀態,成功返回
            if (compareAndSetState(c, c + SHARED_UNIT)) {
                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;
            }
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章