ReentrantReadWriteLock源碼分析及理解_網頁設計公司

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

本文結構

  • 讀寫鎖簡介:介紹讀寫鎖、讀寫鎖的特性以及類定義信息
  • 公平策略及Sync同步器:介紹讀寫鎖提供的公平策略以及同步器源碼分析
  • 讀鎖:介紹讀鎖的一些常用操作和讀鎖的加鎖、解鎖的源碼分析
  • 寫鎖:介紹寫鎖的一些常用操作和寫鎖的加鎖、解鎖的源碼分析
  • 總結:總結全文,附讀寫鎖全部源碼理解

讀寫鎖簡介

在之前的文章提到了可重入鎖,這是一種排他鎖,核心原理是同一時間只允許一個線程訪問。除了排他鎖還有一種共享鎖,這種鎖在同一時間支持多線程同時訪問,
將排他鎖和共享鎖進行組合便有了讀寫鎖。讀寫鎖維護了一組鎖——讀鎖和寫鎖。讀鎖在同一時間可以有多個線程共同訪問,是一個共享鎖;而寫鎖在同一時間僅支持
一個線程訪問,是一個排他鎖。通過讀鎖的允許多個線程同時訪問,使得併發性相比單純的排他鎖效率有很大的提升。

在讀寫鎖中,需要保證寫鎖對於讀鎖的可見性,也就是說當寫鎖更改數據之後讀鎖需要能夠立刻獲知。假設有一組線程訪問同一個緩存區,其中只有一個線程向其中寫數據,
其他的線程都是讀數據,這塊區域大部分的時間都是用戶的讀操作,只有很少的時間是寫操作,多個線程的讀並不會相互影響,那麼就可以使用讀寫鎖,只需要保證寫操作
之後,數據立刻對於其他的讀操作可見即可。

\(\color{#FF3030}{讀寫鎖是一個鎖,只是可以進行共享或者排他的兩種操作模式。}\)讀寫鎖是一個鎖,只是可以進行共享或者排他的兩種操作模式。

讀寫鎖的特性

一般情況下,讀寫鎖的性能會優於排他鎖,因為程序中大多數場景都是讀取數據,很少一部分是寫數據。在讀取併發多的情況下,可以提供比排他鎖更好的吞吐量。

特性 說明
公平性 讀寫鎖可以選擇公平和非公平性兩種特性,默認為非公平模式,並且吞吐量非公平由於公平模式
可重入性 讀(寫)鎖支持線程的重入。當一個線程獲取讀(寫)鎖后,這個線程可以再次獲取這個讀(寫)鎖
鎖降級 當一個線程獲取寫鎖之後,可以獲取讀鎖,在釋放寫鎖完成鎖降級過程

讀寫鎖的定義

ReentrantReadWriteLock簡單分析,主要介紹類由哪些部分組成及每部分的作用,具體的實現後面按照內部類及提供主要操作細解

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
    private static final long serialVersionUID = -6992448646407690164L;
    /** Inner class providing readlock */
    //讀鎖,讀鎖類是讀寫鎖的內部類
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** Inner class providing writelock */
    //寫鎖,寫鎖類是讀寫鎖的內部類
    private final ReentrantReadWriteLock.WriteLock writerLock;
    /** Performs all synchronization mechanics */
    //同步器,完成核心的加鎖釋放鎖的過程,公平機制委派其子類實現
    final Sync sync;

    /**
     * Creates a new {@code ReentrantReadWriteLock} with
     * default (nonfair) ordering properties.
     */
    //默認的讀寫鎖構造函數,默認使用非公平模式
    public ReentrantReadWriteLock() {
        this(false);
    }

    /**
     * Creates a new {@code ReentrantReadWriteLock} with
     * the given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    //帶公平策略選擇的構造器,其中參數為true代表公平模式,false代表非公平模式
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }
    
    //獲取寫鎖
    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }

    //獲取讀鎖
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }
    
    //同步器類,實現核心的加鎖解鎖同步隊列的操作,委派子類實現公平非公平策略
    abstract static class Sync extends AbstractQueuedSynchronizer {......}

    //非公平模式同步器,繼承Sync並實現非公平策略
    static final class NonfairSync extends Sync{....}

    //公平模式同步器,繼承Sync並實現公平策略
    static final class FairSync extends Sync{....}

    //寫鎖類,使用與外部類一致的公平策略
    public static class WriteLock implements Lock, java.io.Serializable{......}

    //讀鎖類,使用與外部類一致的公平策略
    public static class ReadLock implements Lock, java.io.Serializable{......}

公平策略及Sync同步器

讀寫鎖提供了公平與非公平策略,並由Sync的子類實現。NonfairSyncFairSync主要用來判斷獲取讀鎖和寫鎖的時候是否需要阻塞,
其獲取鎖的過程全部交由Sync實現。也可以說使Sync分為公平與非公平兩個版本。

非公平策略

非公平版本的同步器,詳細解釋見源碼註釋

//公平版本的同步器
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();
    }
}

//這個方法在AQS中實現,目的就是執行的操作就是判斷隊列的頭節點的後繼節點是不是獲取寫鎖
final boolean apparentlyFirstQueuedIsExclusive() {
    Node h, s;
    return (h = head) != null &&
        (s = h.next)  != null &&
        !s.isShared()         &&
        s.thread != null;
}

公平策略

公平版本的同步器,詳細解釋見源碼註釋

//相比非公平版本就會簡單很多,只需要判斷隊列中是否有現成在等待就可以
static final class FairSync extends Sync {
        private static final long serialVersionUID = -2274990926593161451L;
        final boolean writerShouldBlock() {
            return hasQueuedPredecessors();
        }
        final boolean readerShouldBlock() {
            return hasQueuedPredecessors();
        }
    }

Sync同步器

Sync實現了主要的加鎖解鎖的操作。讀寫鎖同樣依賴AQS來實現同步功能,在AQS中由State整型字段代表所數量而這裡是兩種鎖,因此使用位操作來代表不同的鎖,
使用高16位代表共享鎖(讀鎖),低16位代表排他鎖(寫鎖)如下圖,並且所有與為操有關的事情都在這裏完成,AQS中僅提供一些判斷接口及隊列操作。

具體代碼解釋見註釋

abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 6317671515068378041L;

    /*
     * 讀鎖和寫鎖的一些常數和函數
     * 將鎖狀態在邏輯上分為兩個無符號的短整型
     * 低位代表排他鎖(寫鎖)的計數器
     * 高位代表共享鎖(讀鎖)的計數器
     */

    //共享鎖的偏移量——16位
    static final int SHARED_SHIFT   = 16;
    //共享鎖計數器加一或者減一的基數
    static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
    //鎖計數器的最大值,做最大可以被同時或者重入獲取的次數
    static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
    //寫鎖的掩碼 和鎖狀態state按位與操作可以得到寫鎖的計數器
    static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

    //計算共享鎖的持有量(讀鎖)
    static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
    //計算排他鎖的持有量(寫鎖)
    static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

    /**
     * 每個線程持有的讀鎖的計數器.
     * 作為ThreadLocal維護; 最後一成功加鎖的線程信息緩存在cachedHoldCounter
     */
    static final class HoldCounter {
        //線程持有的讀鎖的數量
        int count = 0;
        // Use id, not reference, to avoid garbage retention
        //使用id而不使用引用避免垃圾保留
        final long tid = getThreadId(Thread.currentThread());
    }

    /**
     * ThreadLocal 子類. 重寫了initialValue方法,
     * 在第一次get時(之前也沒進行過set操作)返回count值為0而不是null
     */
    static final class ThreadLocalHoldCounter
        extends ThreadLocal<HoldCounter> {
        public HoldCounter initialValue() {
            return new HoldCounter();
        }
    }

    /**
     * The number of reentrant read locks held by current thread.
     * Initialized only in constructor and readObject.
     * Removed whenever a thread's read hold count drops to 0.
     */
    //讀鎖被當前線程持有的次數,僅在構造函數和readObject中初始化
    //當線程的讀鎖持有數量為0時刪除
    private transient ThreadLocalHoldCounter readHolds;

    /**
     * The hold count of the last thread to successfully acquire
     * readLock. This saves ThreadLocal lookup in the common case
     * where the next thread to release is the last one to
     * acquire. This is non-volatile since it is just used
     * as a heuristic, and would be great for threads to cache.
     *
     * <p>Can outlive the Thread for which it is caching the read
     * hold count, but avoids garbage retention by not retaining a
     * reference to the Thread.
     *
     * <p>Accessed via a benign data race; relies on the memory
     * model's final field and out-of-thin-air guarantees.
     */
    //最後一個成功獲取讀鎖的線程持有的讀鎖的數量
    private transient HoldCounter cachedHoldCounter;

    /**
     * firstReader is the first thread to have acquired the read lock.
     * firstReaderHoldCount is firstReader's hold count.
     *
     * <p>More precisely, firstReader is the unique thread that last
     * changed the shared count from 0 to 1, and has not released the
     * read lock since then; null if there is no such thread.
     *
     * <p>Cannot cause garbage retention unless the thread terminated
     * without relinquishing its read locks, since tryReleaseShared
     * sets it to null.
     *
     * <p>Accessed via a benign data race; relies on the memory
     * model's out-of-thin-air guarantees for references.
     *
     * <p>This allows tracking of read holds for uncontended read
     * locks to be very cheap.
     */
    //最後一個將讀鎖計數器從0改為1的線程,並且一直沒有釋放讀鎖
    //如果不存在這個線程則為null
    private transient Thread firstReader = null;
    private transient int firstReaderHoldCount;

    //構造函數
    Sync() {
        readHolds = new ThreadLocalHoldCounter();
        setState(getState()); // ensures visibility of readHolds
    }

    /*
     * Acquires and releases use the same code for fair and
     * nonfair locks, but differ in whether/how they allow barging
     * when queues are non-empty.
     */

    /**
     * Returns true if the current thread, when trying to acquire
     * the read lock, and otherwise eligible to do so, should block
     * because of policy for overtaking other waiting threads.
     */
    //獲取和釋放讀寫鎖,公平版本和非公平版本使用同樣的代碼結構
    //但在當前線程是否需要排隊阻塞,如何阻塞方面存在差異
    //返回true表示當前線程試圖獲取讀鎖應當被阻塞,
    abstract boolean readerShouldBlock();

    /**
     * Returns true if the current thread, when trying to acquire
     * the write lock, and otherwise eligible to do so, should block
     * because of policy for overtaking other waiting threads.
     */
    //true表示當前線程嘗試獲取寫鎖應該被阻塞
    abstract boolean writerShouldBlock();

    /*
     * Note that tryRelease and tryAcquire can be called by
     * Conditions. So it is possible that their arguments contain
     * both read and write holds that are all released during a
     * condition wait and re-established in tryAcquire.
     */
    
    //releases:釋放寫鎖的次數,該值小於等於當前線程持有的寫鎖的數量
    //返回這個線程是否繼續持有這個寫鎖
    protected final boolean tryRelease(int releases) {
        
        //判斷當前線程是否持有這個排他鎖
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        //計算釋放之後持有這個鎖的次數
        int nextc = getState() - releases;
        boolean free = exclusiveCount(nextc) == 0;
        //釋放后不在持有這個寫鎖
        if (free)
            //設置鎖屬於的線程為null
            setExclusiveOwnerThread(null);
        //設置鎖的狀態
        setState(nextc);
        return free;
    }

    //嘗試獲取寫鎖
    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.
         */
        /*
         * 需要完成的工作:
         * 1. 如果鎖狀態(包含讀鎖和寫鎖)不為0,並且當前線程沒有持有寫鎖則失敗
         * 2. 如果寫鎖計數器大於最大值則獲取失敗
         * 3. 否則如果是重入的獲取鎖,則會被允許.
         */
        Thread current = Thread.currentThread();
        //獲取鎖狀態
        int c = getState();
        //獲取寫鎖的鎖狀態
        int w = exclusiveCount(c);
        //如果鎖被不為null即可能被任何一個線程持有
        if (c != 0) {
            // (Note: if c != 0 and w == 0 then shared count != 0)
            //w==0則讀鎖被某個線程持有或者寫鎖被其他線程持有則獲取鎖失敗
            //進入隊列排隊
            if (w == 0 || current != getExclusiveOwnerThread())
                return false;

            //寫所已經被當前線程持有則判斷再次加鎖后是否會超過寫鎖的最大可以被加鎖的次數
            //超過則加鎖失敗
            if (w + exclusiveCount(acquires) > MAX_COUNT)
                throw new Error("Maximum lock count exceeded");
            // Reentrant acquire
            //重入的獲取鎖
            setState(c + acquires);
            return true;
        }
        //鎖沒有被任何線程持有,則需要根據公平策略來判斷當前線程是否需要阻塞
        //公平鎖:檢查同步等待隊列,若隊列中存在等待時間更長的線程則需要阻塞
        //非公平鎖:可以搶佔式獲取寫鎖,不需要阻塞

        //需要被阻塞或者CAs操作失敗則進入同步隊列
        if (writerShouldBlock() ||
            !compareAndSetState(c, c + acquires))
            return false;
        //不需要阻塞並且加鎖成功,設置排他鎖的所屬線程信息
        setExclusiveOwnerThread(current);
        return true;
    }

    //嘗試釋放共享鎖,與加共享鎖一致,只能一個一個的釋放
    //unused參數沒有被使用
    protected final boolean tryReleaseShared(int unused) {
        Thread current = Thread.currentThread();
        //當前線程是firstReader並且僅持有一次讀鎖,在釋放讀鎖后firstReader應該置null
        if (firstReader == current) {
            // assert firstReaderHoldCount > 0;
            if (firstReaderHoldCount == 1)
                firstReader = null;
            else
                firstReaderHoldCount--;
        } else {
            //獲取當前線程的HoldCounter信息
            HoldCounter rh = cachedHoldCounter;
            if (rh == null || rh.tid != getThreadId(current))
                rh = readHolds.get();
            int count = rh.count;
            //當讀誦持有數量歸零時,會從線程的threadLocals中刪除readHolds
            if (count <= 1) {
                readHolds.remove();
                //沒持有鎖的縣城不能釋放鎖
                if (count <= 0)
                    throw unmatchedUnlockException();
            }
            --rh.count;
        }
        //使用cas操作原子的減少鎖狀態,避免CAS操作失敗的情況
        for (;;) {
            int c = getState();
            //減少一個讀鎖
            int nextc = c - SHARED_UNIT;
            if (compareAndSetState(c, nextc))
                // 釋放讀鎖對於讀操作沒有影響,
                // 但是如果現在讀鎖和寫鎖都是空閑的
                // 可能會使等待的獲取寫鎖的操作繼續
                
                //返回鎖是否還被任何一個線程持有
                return nextc == 0;
        }
    }

    private IllegalMonitorStateException unmatchedUnlockException() {
        return new IllegalMonitorStateException(
            "attempt to unlock read lock, not locked by current thread");
    }

    //獲取讀鎖
    //unused參數沒有被使用,一個一個加鎖
    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.
         */
        /*
         * 待辦事項:
         * 1. 如果寫鎖被其他線程獲取,則失敗.
         * 2. 否則這個線程就有資格使用鎖的狀態,因此需要判斷是否因為
         *    因為同步等待策略而阻塞,否則嘗通過cas操作嘗試授予鎖
         *    可重入性的操作在fullTryAcquireShared中進行
         *    避免在不可重入的情況下檢查鎖狀態
         * 3. 如果步驟2失敗,因為線程不符合條件或者cas失敗
         *    則進入fullTryAcquireShared中循環重試
         */
        Thread current = Thread.currentThread();
        int c = getState();
            //寫鎖被其他線程持有則獲取讀鎖失敗,需要進入同步等待隊列
        if (exclusiveCount(c) != 0 &&
            getExclusiveOwnerThread() != current)
            return -1;
        int r = sharedCount(c);
            
            /**
             * 判斷當前線程是否需要阻塞
             * 不同的公平策略有不同的判斷方式
             * 非公平模式下,如果存在同步等待隊列且第一個是嘗試獲取寫鎖的
             * 其他線程則需要阻塞
             * 公平模式下,隊列中存在排隊等待的線程則需要進入隊列等待
             */
    
            //如果當前線程已經獲取樂寫鎖,則這可以是一個鎖降級的過程
            //不用進入隊列排隊
        if (!readerShouldBlock() &&
            //鎖的獲取次數不能超過最大的可獲取的次數
            r < MAX_COUNT &&
            //不需要阻塞,鎖的計數沒有超過最大值則嘗試通過cas操作加鎖
            //可能會失敗,如果存在多個線程同時競爭
            compareAndSetState(c, c + SHARED_UNIT)) {
            //加鎖成功,判斷是否是第一個加鎖的線程,是則設置firstReader信息
            //firstReader信息將不會在threadLocals中保存
            if (r == 0) {
                firstReader = current;
                firstReaderHoldCount = 1;
    
                //firstReader的重入情況
            } else if (firstReader == current) {
                firstReaderHoldCount++;
            } else {
                //當前線程是最後一個獲取讀鎖的線程,
                //需要將當前線程設置為cachedHoldCounter
                HoldCounter rh = cachedHoldCounter;
                //當前線程不是在此之前最後一次獲取讀鎖的線程
                //需要從ThreadLocals中獲取當前鎖的計數信息
                //並且將當前線程設置為最後一個獲取讀鎖的線程
                if (rh == null || rh.tid != getThreadId(current))
                    cachedHoldCounter = rh = readHolds.get();
                //如果當前線程就是在此之前最後一次獲取讀鎖的信息
                //並且鎖計數器為0,則需要設置當前線程的threadLcoals中保存的鎖計數信息
                //因為鎖計數器為0的時候會從ThreadLocals中刪除readHolds的信息
                else if (rh.count == 0)
                    readHolds.set(rh);
                rh.count++;
            }
            return 1;
        }
        //需要被阻塞、鎖計數器超過最大值、或者cas設置鎖狀態失敗
        //進入完整版本的獲取鎖的過程
        return fullTryAcquireShared(current);
    }

    /**
     * Full version of acquire for reads, that handles CAS misses
     * and reentrant reads not dealt with in 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
    
                //當前線程是firstReader再次獲取讀鎖
                //firstReader變量存在的前提是獲取讀鎖的線程沒有被釋放讀鎖
                //則是一種重入的情況,可以直接判斷並增加鎖計數器
                if (firstReader == current) {
                    // assert firstReaderHoldCount > 0;
                
                //當前線程不是firstReader
                } else {
                    if (rh == null) {
                        //rh:當前線程對應的鎖計數器信息
                        //在當前線程的threadLocals中存儲
                        rh = cachedHoldCounter;
                        if (rh == null || rh.tid != getThreadId(current)) {
                            rh = readHolds.get();
                            //當前線程沒有獲取鎖,從threadLocals中移除這個鎖信息
                            //因為readHolds.get()從當前線程的threadLocals中獲取HoldCounter對象時
                            //如果threadLocals中不存在當前鎖的狀態信息,get的時候會初始化一個,count=0
                            if (rh.count == 0)
                                readHolds.remove();
                        }
                    }
                    //當前線程不是重入的獲取鎖
                    //並且同步等待隊列的第一個等待節點嘗試獲取寫鎖。且不失當前線程
                    //當前線程需要排隊等待
                    //目的:避免寫鎖的無限及飢餓
                    //當前線程已經獲取鎖
                    if (rh.count == 0)
                        return -1;
                }
            }
            //可以獲取讀鎖的情況:寫鎖被當前線程獲取或者重入的獲取鎖
            //或者不用阻塞寫鎖也沒有被其他線程獲取,到這裏的原因可能是tryAcquireShared中CAS操作失敗
            //如果是當前線程已經獲取樂寫鎖,則這是一個鎖降級的過程
          
            //超過讀鎖計數器的最大值
            if (sharedCount(c) == MAX_COUNT)
                throw new Error("Maximum lock count exceeded");
    
            //cas的獲取鎖,如果cas操作失會循環獲取
            if (compareAndSetState(c, c + SHARED_UNIT)) {
                //如果當前線程是將讀鎖從0->1,則是firstReader
                if (sharedCount(c) == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                //firstReader重入的獲取鎖
                } 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
                    cachedHoldCounter = rh; // cache for release
                }
                return 1;
            }
        }
    }

    /**
     * Performs tryLock for write, enabling barging in both modes.
     * This is identical in effect to tryAcquire except for lack
     * of calls to writerShouldBlock.
     */
    final boolean tryWriteLock() {
        Thread current = Thread.currentThread();
        int c = getState();
        if (c != 0) {
            int w = exclusiveCount(c);
            if (w == 0 || current != getExclusiveOwnerThread())
                return false;
            if (w == MAX_COUNT)
                throw new Error("Maximum lock count exceeded");
        }
        if (!compareAndSetState(c, c + 1))
            return false;
        setExclusiveOwnerThread(current);
        return true;
    }

    /**
     * Performs tryLock for read, enabling barging in both modes.
     * This is identical in effect to tryAcquireShared except for
     * lack of calls to readerShouldBlock.
     */
    final boolean tryReadLock() {
        Thread current = Thread.currentThread();
        for (;;) {
            int c = getState();
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return false;
            int r = sharedCount(c);
            if (r == MAX_COUNT)
                throw new Error("Maximum lock count exceeded");
            if (compareAndSetState(c, c + SHARED_UNIT)) {
                if (r == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } 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 true;
            }
        }
    }

    //返回當前線程是否持有寫鎖
    protected final boolean isHeldExclusively() {
        // While we must in general read state before owner,
        // we don't need to do so to check if current thread is owner
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

    // Methods relayed to outer class
    //返回一個與鎖關聯的Condition
    final ConditionObject newCondition() {
        return new ConditionObject();
    }

    //獲取持有寫鎖的線程
    final Thread getOwner() {
        // Must read state before owner to ensure memory consistency
        return ((exclusiveCount(getState()) == 0) ?
                null :
                getExclusiveOwnerThread());
    }
    
    //獲取所有線程持有的總的讀鎖的數量
    final int getReadLockCount() {
        return sharedCount(getState());
    }

    final boolean isWriteLocked() {
        return exclusiveCount(getState()) != 0;
    }

    獲取當前線程持有的寫鎖的數量
    final int getWriteHoldCount() {
        return isHeldExclusively() ? exclusiveCount(getState()) : 0;
    }

    //獲取當前線程持有的讀鎖的數量
    final int getReadHoldCount() {
        if (getReadLockCount() == 0)
            return 0;

        //如果當前線程是firstReader則直接返回
        Thread current = Thread.currentThread();
        if (firstReader == current)
            return firstReaderHoldCount;

        //如果當前線程是最後一個持有讀鎖的線程
        HoldCounter rh = cachedHoldCounter;
        if (rh != null && rh.tid == getThreadId(current))
            return rh.count;

        //獲取當前線程的HoldCounter中存儲的持有鎖的數量
        int count = readHolds.get().count;
        if (count == 0) readHolds.remove();
        return count;
    }

    /**
     * Reconstitutes the instance from a stream (that is, deserializes it).
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        readHolds = new ThreadLocalHoldCounter();
        setState(0); // reset to unlocked state
    }

    final int getCount() { return getState(); }
}

讀鎖

讀鎖是一個支持重入的共享鎖,只要沒有超過鎖計數器的範圍,鎖可以被任意多個線程獲取。
鎖狀態為0且寫鎖沒由被獲取的情況下,讀鎖總能被獲取,後續的獲取操作也只是原子的增加鎖計數器。
但是由於不同線程都持有鎖,需要保存每個線程持有這個鎖的次數,這就用到了ThreadLocalHoldCounter將線程的持有鎖的次數保存在
現成的threadLocals字段中。

讀鎖的定義

讀鎖的定義很簡單,讀鎖中也存在一個同步器,與外部類同步器保持一致,所有的操作交由同步器完成

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

public static class ReadLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = -5992448646407690164L;
    private final Sync sync;

    /**
     * Constructor for use by subclasses
     *
     * @param lock the outer lock object
     * @throws NullPointerException if the lock is null
     */
    protected ReadLock(ReentrantReadWriteLock lock) {
        sync = lock.sync;
    }
    ......
}

讀鎖中的一些方法

方法名 方法描述
lock() 嘗試獲取讀鎖,根據不同的公平策略,有不同的獲取過程,忽略中斷
lockInterruptibly() 同上,會響應中斷,實現上是在獲取鎖和在隊列中從阻塞喚醒后判斷是否發生中斷,若有則拋出中斷異常
tryLock() 搶佔式的嘗試一次獲取讀鎖,失敗則返回不會進入同步等待隊列
tryLock(long timeout, TimeUnit unit) 嘗試獲取讀鎖,會響應中斷,同時具有超時功能,實現上是在lockInterruptibly()的基礎上使用具有超時功能的LockSupport.parkNanos(Object blocker, long nanos)
unlock() 釋放一個獲取的讀鎖
newCondition() 構造一個條件等待隊列,在使用條件等待隊列阻塞前,會釋放所有的鎖

讀鎖加鎖

/**
 * Acquires the read lock.
 *
 * <p>Acquires the read lock if the write lock is not held by
 * another thread and returns immediately.
 *
 * <p>If the write lock is held by another thread then
 * the current thread becomes disabled for thread scheduling
 * purposes and lies dormant until the read lock has been acquired.
 */
//申請讀鎖
//如果寫鎖沒有被其他線程持有,則立刻獲得讀鎖並返回
//如果寫鎖沒有被其他線程持有,處於線程調度的目的,當前線程會進入waiting狀態直到獲取到鎖
public void lock() {
    sync.acquireShared(1);
}

acquireShared()方法:至少會調用一次同步器的tryAcquireShared()方法

/**
 * Acquires in shared mode, ignoring interrupts.  Implemented by
 * first invoking at least once {@link #tryAcquireShared},
 * returning on success.  Otherwise the thread is queued, possibly
 * repeatedly blocking and unblocking, invoking {@link
 * #tryAcquireShared} until success.
 *
 * @param arg the acquire argument.  This value is conveyed to
 *        {@link #tryAcquireShared} but is otherwise uninterpreted
 *        and can represent anything you like.
 */
//以忽略中斷、共享模式獲取鎖,至少會調用一次tryAcquireShared方法,成功獲取鎖則返回1,
//失敗則返回-1,線程會進入同步等待隊列,滿足條件時會被阻塞直到前一個線程(寫鎖)釋放或者(讀鎖)被獲取
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
//tryAcquireShared方法由同步器重寫獲取過程,是真正的獲取鎖的過程
//doAcquireShared中獲取鎖的步驟依然是交給tryAcquireShared完成,自身更多完成排隊等待的動作

tryAcquireShared()方法

protected final int tryAcquireShared(int unused) {
    /*
     * 待辦事項:
     * 1. 如果寫鎖被其他線程獲取,則失敗.
     * 2. 否則這個線程就有資格使用鎖的狀態,因此需要判斷是否因為
     *    因為同步等待策略而阻塞,否則嘗通過cas操作嘗試授予鎖
     *    可重入性的操作在fullTryAcquireShared中進行
     *    避免在不可重入的情況下檢查鎖狀態
     * 3. 如果步驟2失敗,因為線程不符合條件或者cas失敗
     *    則進入fullTryAcquireShared中循環重試
     */
    Thread current = Thread.currentThread();
    int c = getState();
        //寫鎖被其他線程持有則獲取讀鎖失敗,需要進入同步等待隊列
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    int r = sharedCount(c);
        
        /**
         * 判斷當前線程是否需要阻塞
         * 不同的公平策略有不同的判斷方式
         * 非公平模式下,如果存在同步等待隊列且第一個是嘗試獲取寫鎖的
         * 其他線程則需要阻塞
         * 公平模式下,隊列中存在排隊等待的線程則需要進入隊列等待
         */

        //如果當前線程已經獲取樂寫鎖,則這可以是一個鎖降級的過程
        //不用進入隊列排隊
    if (!readerShouldBlock() &&
        //鎖的獲取次數不能超過最大的可獲取的次數
        r < MAX_COUNT &&
        //不需要阻塞,鎖的計數沒有超過最大值則嘗試通過cas操作加鎖
        //可能會失敗,如果存在多個線程同時競爭
        compareAndSetState(c, c + SHARED_UNIT)) {
        //加鎖成功,判斷是否是第一個加鎖的線程,是則設置firstReader信息
        //firstReader信息將不會在threadLocals中保存
        if (r == 0) {
            firstReader = current;
            firstReaderHoldCount = 1;

            //firstReader的重入情況
        } else if (firstReader == current) {
            firstReaderHoldCount++;
        } else {
            //當前線程是最後一個獲取讀鎖的線程,
            //需要將當前線程設置為cachedHoldCounter
            HoldCounter rh = cachedHoldCounter;
            //當前線程不是在此之前最後一次獲取讀鎖的線程
            //需要從ThreadLocals中獲取當前鎖的計數信息
            //並且將當前線程設置為最後一個獲取讀鎖的線程
            if (rh == null || rh.tid != getThreadId(current))
                cachedHoldCounter = rh = readHolds.get();
            //如果當前線程就是在此之前最後一次獲取讀鎖的信息
            //並且鎖計數器為0,則需要設置當前線程的threadLcoals中保存的鎖計數信息
            //因為鎖計數器為0的時候會從ThreadLocals中刪除readHolds的信息
            else if (rh.count == 0)
                readHolds.set(rh);
            rh.count++;
        }
        return 1;
    }
    //需要被阻塞、鎖計數器超過最大值、或者cas設置鎖狀態失敗
    //進入完整版本的獲取鎖的過程
    return fullTryAcquireShared(current);
}

fullTryAcquireShared()方法

/**
 * 獲取讀鎖操作的完整版本,處理CAS遺漏和可重入讀操作,
 * 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

            //當前線程是firstReader再次獲取讀鎖
            //firstReader變量存在的前提是獲取讀鎖的線程沒有被釋放讀鎖
            //則是一種重入的情況,可以直接判斷並增加鎖計數器
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
            
            //當前線程不是firstReader
            } else {
                if (rh == null) {
                    //rh:當前線程對應的鎖計數器信息
                    //在當前線程的threadLocals中存儲
                    rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current)) {
                        rh = readHolds.get();
                        //當前線程沒有獲取鎖,從threadLocals中移除這個鎖信息
                        //因為readHolds.get()從當前線程的threadLocals中獲取HoldCounter對象時
                        //如果threadLocals中不存在當前鎖的狀態信息,get的時候會初始化一個,count=0
                        if (rh.count == 0)
                            readHolds.remove();
                    }
                }
                //當前線程不是重入的獲取鎖
                //並且同步等待隊列的第一個等待節點嘗試獲取寫鎖。且不失當前線程
                //當前線程需要排隊等待
                //目的:避免寫鎖的無限及飢餓
                //當前線程已經獲取鎖
                if (rh.count == 0)
                    return -1;
            }
        }
        //可以獲取讀鎖的情況:寫鎖被當前線程獲取或者重入的獲取鎖
        //或者不用阻塞寫鎖也沒有被其他線程獲取,到這裏的原因可能是tryAcquireShared中CAS操作失敗
        //如果是當前線程已經獲取樂寫鎖,則這是一個鎖降級的過程
      
        //超過讀鎖計數器的最大值
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");

        //cas的獲取鎖,如果cas操作失會循環獲取
        if (compareAndSetState(c, c + SHARED_UNIT)) {
            //如果當前線程是將讀鎖從0->1,則是firstReader
            if (sharedCount(c) == 0) {
                firstReader = current;
                firstReaderHoldCount = 1;
            //firstReader重入的獲取鎖
            } 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
                cachedHoldCounter = rh; // cache for release
            }
            return 1;
        }
    }
}

讀鎖解鎖

/**
 * Attempts to release this lock.
 *
 * <p>If the number of readers is now zero then the lock
 * is made available for write lock attempts.
 */
//釋放讀鎖
public void unlock() {
    sync.releaseShared(1);
}

releaseShared()方法,在AQS中實現

/**
 * Releases in shared mode.  Implemented by unblocking one or more
 * threads if {@link #tryReleaseShared} returns true.
 *
 * @param arg the release argument.  This value is conveyed to
 *        {@link #tryReleaseShared} but is otherwise uninterpreted
 *        and can represent anything you like.
 * @return the value returned from {@link #tryReleaseShared}
 */
//釋放共享鎖,
//這一步需要完成兩個任務:1-釋放當前線程的讀鎖,
//2-如果讀鎖不再被任何線程持有則喚醒同步等待隊列中等待的節點(如果有)
//如果tryReleaseShared返回的是true,則解除同步等待隊列中的一個或者多個阻塞的節點
//tryReleaseShared返回true代表讀鎖不再被任何線程持有
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

tryReleaseShared()方法

//嘗試釋放共享鎖,與加共享鎖一致,只能一個一個的釋放
//unused參數沒有被使用
protected final boolean tryReleaseShared(int unused) {
    Thread current = Thread.currentThread();
    //當前線程是firstReader並且僅持有一次讀鎖,在釋放讀鎖后firstReader應該置null
    if (firstReader == current) {
        // assert firstReaderHoldCount > 0;
        if (firstReaderHoldCount == 1)
            firstReader = null;
        else
            firstReaderHoldCount--;
    } else {
        //獲取當前線程的HoldCounter信息
        HoldCounter rh = cachedHoldCounter;
        if (rh == null || rh.tid != getThreadId(current))
            rh = readHolds.get();
        int count = rh.count;
        //當讀誦持有數量歸零時,會從線程的threadLocals中刪除readHolds
        if (count <= 1) {
            readHolds.remove();
            //沒持有鎖的縣城不能釋放鎖
            if (count <= 0)
                throw unmatchedUnlockException();
        }
        --rh.count;
    }
    //使用cas操作原子的減少鎖狀態,避免CAS操作失敗的情況
    for (;;) {
        int c = getState();
        //減少一個讀鎖
        int nextc = c - SHARED_UNIT;
        if (compareAndSetState(c, nextc))
            // 釋放讀鎖對於讀操作沒有影響,
            // 但是如果現在讀鎖和寫鎖都是空閑的
            // 可能會使等待的獲取寫鎖的操作繼續
            
            //返回鎖是否還被任何一個線程持有
            return nextc == 0;
    }
}

doReleaseShared()方法,在AQS中實現,用於喚醒隊列中的後繼節點,
這個方法的出口需要一些解釋,具體內容參看doReleaseShared()

寫鎖

寫鎖是一個支持重入的排他鎖,隊列的操作與可重入鎖相同,可以作為參考,區別是獲取鎖的過程和進入隊列的條件

寫鎖的定義

寫鎖的變臉與讀鎖相同,僅有一個同步器字段,所有的操作調用同步器對應的方法完成

public static class WriteLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = -4992448646407690164L;
    private final Sync sync;

    /**
     * Constructor for use by subclasses
     *
     * @param lock the outer lock object
     * @throws NullPointerException if the lock is null
     */
    protected WriteLock(ReentrantReadWriteLock lock) {
        sync = lock.sync;
    }
    .......
}

寫鎖的一些方法

方法名 方法描述
lock() 嘗試獲取寫鎖,根據不同的公平策略,有不同的獲取過程,忽略中斷
lockInterruptibly() 同上,會響應中斷,實現上是在獲取鎖和在隊列中從阻塞喚醒后判斷是否發生中斷,若有則拋出中斷異常
tryLock() 搶佔式的嘗試一次獲取一次寫鎖,失敗則返回不會進入同步等待隊列
tryLock(long timeout, TimeUnit unit) 嘗試獲取寫鎖,會響應中斷,同時具有超時功能,實現上是在lockInterruptibly()的基礎上使用具有超時功能的LockSupport.parkNanos(Object blocker, long nanos)
unlock() 釋放一個獲取的寫鎖
newCondition() 構造一個條件等待隊列,在使用條件等待隊列阻塞前,會釋放所有的鎖
isHeldByCurrentThread() 判斷當前線程是否持有寫鎖
getHoldCount 獲取當前線程重入性獲取當前鎖的次數

寫鎖得加鎖

寫鎖是一個排他鎖,總體思路感覺可以說是可重入鎖和讀鎖得結合,寫鎖獲取成功時要求讀鎖沒有被獲取並且寫鎖沒有被其他線程獲取,
類似於可重入鎖,一定是獨佔的。考慮到讀寫鎖是一對鎖,因此在加鎖的時候肯定是需要判斷讀鎖有沒有被獲取。
讀寫鎖任意一個被其他線程獲取,都將導致線程進入同步等待隊列等候。

加鎖:

/**
 * Acquires the write lock.
 *
 * <p>Acquires the write lock if neither the read nor write lock
 * are held by another thread
 * and returns immediately, setting the write lock hold count to
 * one.
 *
 * <p>If the current thread already holds the write lock then the
 * hold count is incremented by one and the method returns
 * immediately.
 *
 * <p>If the lock is held by another thread then the current
 * thread becomes disabled for thread scheduling purposes and
 * lies dormant until the write lock has been acquired, at which
 * time the write lock hold count is set to one.
 */
//嘗試獲取寫鎖,如果讀鎖和寫鎖都沒有被其他線程持有,則可以獲取寫鎖。
//如果當前線程已經獲取了寫鎖,則鎖計數器加一併返回
//如果鎖由其他線程持有,那麼線程將入同步等待隊列阻塞,直到可以獲取鎖
public void lock() {
    sync.acquire(1);
}

acquire()方法,在AQS中實現,這個就和可重入鎖的一樣了,由AQS完成隊列的操作

/**
 * Acquires in exclusive mode, ignoring interrupts.  Implemented
 * by invoking at least once {@link #tryAcquire},
 * returning on success.  Otherwise the thread is queued, possibly
 * repeatedly blocking and unblocking, invoking {@link
 * #tryAcquire} until success.  This method can be used
 * to implement method {@link Lock#lock}.
 *
 * @param arg the acquire argument.  This value is conveyed to
 *        {@link #tryAcquire} but is otherwise uninterpreted and
 *        can represent anything you like.
 */
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

tryAcquire()方法,實際獲取寫鎖的方法,這份方法在Sync中實現,這裏需要解釋一個問題:

  • 在獲取寫鎖的時候,若之前已經獲取了讀鎖,則一定要釋放讀鎖再獲取讀鎖。如果不釋放讀鎖,則可能造成死鎖。
    舉個例子:假設有ABCD四個線程獲取了讀鎖且沒釋放,此時線程A嘗試獲取寫鎖。由於讀鎖被線程獲取,線程A將進入同步等待隊列被阻塞,
    此時線程A讀鎖依然沒有被釋放,讀鎖釋放時喚醒後繼節點的條件是讀鎖不被任何線程獲取,此時讀鎖依然被A持有,且A在阻塞。
    由於線程A在同步等待隊列的隊頭且獲取寫鎖,則之後所有的獲取鎖的線程都將被阻塞。至此死鎖。
//嘗試獲取寫鎖
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.
     */
    /*
     * 需要完成的工作:
     * 1. 如果鎖狀態(包含讀鎖和寫鎖)不為0,並且當前線程沒有持有寫鎖則失敗
     * 2. 如果寫鎖計數器大於最大值則獲取失敗
     * 3. 否則如果是重入的獲取鎖,則會被允許.
     */
    Thread current = Thread.currentThread();
    //獲取鎖狀態
    int c = getState();
    //獲取寫鎖的鎖狀態
    int w = exclusiveCount(c);
    //如果鎖被不為null即可能被任何一個線程持有
    if (c != 0) {
        // (Note: if c != 0 and w == 0 then shared count != 0)
        //w==0則讀鎖被某個線程持有或者寫鎖被其他線程持有則獲取鎖失敗
        //進入隊列排隊
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;

        //寫所已經被當前線程持有則判斷再次加鎖后是否會超過寫鎖的最大可以被加鎖的次數
        //超過則加鎖失敗
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // Reentrant acquire
        //重入的獲取鎖
        setState(c + acquires);
        return true;
    }
    //鎖沒有被任何線程持有,則需要根據公平策略來判斷當前線程是否需要阻塞
    //公平鎖:檢查同步等待隊列,若隊列中存在等待時間更長的線程則需要阻塞
    //非公平鎖:可以搶佔式獲取寫鎖,不需要阻塞

    //需要被阻塞或者CAs操作失敗則進入同步隊列
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    //不需要阻塞並且加鎖成功,設置排他鎖的所屬線程信息
    setExclusiveOwnerThread(current);
    return true;
}

acquireQueued(addWaiter(Node.EXCLUSIVE), arg))與可重入鎖完全相同,可以參考這裏

讀寫鎖的一些其他方法

方法名 方法描述
isFair() 判斷鎖是否使用的是公平模式
getOwner() 獲取持有寫鎖的線程
getReadLockCount() 獲取讀鎖被所有線程持有的次數
isWriteLocked() 判斷寫鎖是否被任意一個線程獲取
isWriteLockedByCurrentThread() 判斷當前線程是否持有寫鎖
getWriteHoldCount() 獲取當前線程重入的獲取寫鎖的次數
getReadHoldCount() 獲取當前線程重入的獲取讀鎖的次數
getQueuedWriterThreads() 獲取在同步等待隊列中等待的獲取寫鎖的線程集合
getQueuedReaderThreads() 獲取在同步等待隊列中等待的獲取讀鎖的線程集合
hasQueuedThreads() 判斷同步等待隊列中是否存在節點(等待的線程)
hasQueuedThread(Thread thread) 判斷給定的線程是否在隊列中
getQueueLength() 獲取同步等待隊列中等待的節點個數
getQueuedThreads() 獲取在同步等待隊列中等待的線程集合
hasWaiters(Condition condition) 判斷給定的條件等待隊列中是否存在等待的節點
getWaitQueueLength(Condition condition) 判斷給定的條件等待隊列中是否存在等待的節點個數
getWaitingThreads(Condition condition) 獲取給定的條件等待隊列中是否存在等待的節點集合

總結

  • 讀寫鎖把對對象的操作劃分為讀操作和寫操作,讀操作通過獲取讀鎖只能讀取對象,寫操作通過獲取寫鎖
    來完成寫操作的排他性。這種鎖相對於排他鎖而言具有更高的併發性。
  • 已經獲取寫鎖的線程可以通過鎖降級來獲取讀鎖
  • 讀寫鎖是一個鎖,只是可以進行共享或者排他的兩種操作模式。
  • 如果一個鎖既沒有加寫鎖,也沒有加讀鎖,隊列中不存在等待的線程,則可以被任意一個線程獲取任意一個鎖
  • 一個鎖被加了讀鎖,則不能有任何線程加寫鎖
  • 一個鎖被加了寫鎖,加寫鎖的線程可以再次加讀鎖
  • 非公平模式下,同步隊列的第一個等待節點是獲取寫鎖,則之後的獲取讀鎖的線程需要等待
  • 公平模式下,只要隊列中存在等待的節點就需要進入隊列等待
  • 讀寫鎖不支持鎖升級

\(\color{#FF3030}{轉載請標明出處}\)

附ReentrantReadWriteLock全部源碼理解:https://www.cnblogs.com/bmilk/p/13044867.html

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

您可能也會喜歡…