前言
之前分析了使用AQS實現的共享鎖和獨佔鎖,今天來分析一下ReentrantReadWriteLock,這個即使用了共享鎖(讀鎖)又使用了獨享鎖(寫鎖)的類。
與Mysql中的S鎖(共享鎖,讀鎖)一樣,ReentrantReadWriteLock
中的讀鎖只允許繼續加讀鎖,而不允許加寫鎖。
而寫鎖則與Mysql中X鎖(排他鎖)一樣,不允許繼續加任何鎖,知道寫鎖被釋放。
今天我們就來分析下ReentrantReadWriteLock
是如何做到的。
ReentrantReadWriteLock 源碼分析
-
構造方法
ReentrantReadWriteLock 支持公平與非公平模式, 這點和ReentrantLock一樣,構造函數中可以通過指定的值傳遞進去。ReentrantReadWriteLock 顧名思義,可重入的讀寫鎖。
/**
* Creates a new {@code KReentrantReadWriteLock} with
* default (nonfair) ordering properties
* 用 nonfair 來構建 read/WriteLock (這裏的 nonfair 指的是當進行獲取 lock 時 若 aqs的syn queue 裏面是否有 Node 節點而決定所採取的的策略)
*/
public ReentrantReadWriteLock(){
this(false);
}
/**
* 構建 ReentrantReadLock
*/
public ReentrantReadWriteLock(boolean fair){
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
/** Inner class providing readlock */
private final ReentrantReadWriteLock.ReadLock readerLock;
/** Inner class providing writelock */
private final ReentrantReadWriteLock.WriteLock writerLock;
和ReentrantLock一樣,是否是公平鎖是看獲取鎖的策略,而策略的實現要看內部類Sync的具體實現。
在ReentrantReadWriteLock
的構造方法中,除了創建一個內部類Sync對象,還創建了內部類ReadLock
對象以及內部類WriteLock
對象。
ReadLock構造方法
private final Sync sync;
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
ReadLock構造方法將ReentrantReadWriteLock的sync屬性賦給ReadLock的sync屬性。
WriteLock構造方法
private final Sync sync;
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
WriteLock構造方法將ReentrantReadWriteLock的sync屬性賦給WriteLock的sync屬性。
我們看到ReadLock與WriteLock都有sync屬性,而且使用的是同一個Sync對象。
-
Sync中一些關鍵的屬性
-
讀寫計數器
/** * ReentrantReadWriteLock 使用 AQS裏面的 state的高低16位來記錄 read /write 獲取的次數(PS: writeLock 是排他的 exclusive, readLock 是共享的 shared ) * 記錄的操作都是通過 CAS 操作(有競爭發生) * * 特點: * 1) 同一個線程可以擁有 writeLock 與 readLock (但必須先獲取 writeLock 再獲取 readLock, 反過來進行獲取會導致死鎖) * 2) writeLock 與 readLock 是互斥的(就像 Mysql 的 X S 鎖) * 3) 在因 先獲取 readLock 然後再進行獲取 writeLock 而導致 死鎖時, 本線程一直卡住在對應獲取 writeLock 的代碼上(因爲 readLock 與 writeLock 是互斥的, 在獲取 writeLock 時監測到現在有線程獲取 readLock , 鎖一會一直在 aqs 的 sync queue 裏面進行等待), 而此時 * 其他的線程想獲取 writeLock 也會一直 block, 而若獲取 readLock 若這個線程以前獲取過 readLock, 則還能繼續 重入 (reentrant), 而沒有獲取 readLock 的線程因爲 aqs syn queue 裏面有獲取 writeLock 的 Node 節點存在會存放在 aqs syn queue 隊列裏面 一直 block */ /** 對 32 位的 int 進行分割 (對半 16) */ static final int SHARED_SHIFT = 16; static final int SHARED_UNIT = (1 << SHARED_SHIFT); // 000000000 00000001 00000000 00000000 static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; // 000000000 00000000 11111111 11111111 static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 000000000 00000000 11111111 11111111 /** Returns the number of shared holds represented in count */ /** 計算 readLock 的獲取次數(包含重入的次數) */ static int sharedCount(int c) { return c >>> SHARED_SHIFT; } // 將字節向右移動 16位, 只剩下 原來的 高 16 位 /** Returns the number of exclusive holds represented in count */ /** 計算 writeLock 的獲取的次數(包括重入的次數) */ static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } // 和EXCLUSIVE_MASK 與 也就是隻取低16位
之前我們說過了讀寫鎖使用的是同一個Sync對象,之前我們分析的獨佔鎖或者共享鎖,都是通過判斷state狀態來完成獲取鎖或者釋放鎖的邏輯的,那麼這裏有獨佔鎖和共享鎖共同存在,依然還是需要state這個一個字段來判斷獲取鎖或釋放鎖的邏輯(我們依舊需要使用一個字段來判斷,因爲讀鎖和寫鎖不是完全孤立的,寫鎖會阻塞寫鎖和讀鎖,讀鎖會阻塞寫鎖,所以用兩個字段來判斷,反而會增加處理複雜度),需要怎樣做呢?Doug Lea大師給出的答案是,state的高16位來記錄 read 獲取的次數,低16位來記錄write獲取的次數,具體的實現非常精彩,通過位操作,巧妙的使用state字段來標識了讀鎖和寫鎖的關係。
-
線程獲取讀鎖次數統計相關屬性
/** - A counter for per-thread read hold counts - Maintained as a ThreadLocal; cached in cachedHoldCounter */ /** - 幾乎每個獲取 readLock 的線程都會含有一個 HoldCounter 用來記錄 線程 id 與 獲取 readLock 的次數 ( writeLock 的獲取是由 state 的低16位 及 AQS中的exclusiveOwnerThread 來進行記錄) - 這裏有個注意點 第一次獲取 readLock 的線程使用 firstReader, firstReaderHoldCount 來進行記錄 - (PS: 不對, 我們想一下爲什麼不 統一用 HoldCounter 來進行記錄呢? 原因: 所有的 HoldCounter 都是放在 ThreadLocal 裏面, 而很多有些場景中只有一個線程獲取 readLock 與 writeLock , 這種情況還用 ThreadLocal 的話那就有點浪費(ThreadLocal.get() 比直接 通過 reference 來獲取數據相對來說耗性能)) */ static final class HoldCounter { int count = 0; // 重複獲取 readLock/writeLock 的次數 // Use id, not reference, to avoid garbage retention final long tid = getThreadId(Thread.currentThread()); // 線程 id } /** - ThreadLocal subclass, Easiest to explicitly define for sake - of deserialization mechanics */ /** 簡單的自定義的 ThreadLocal 來用進行記錄 readLock 獲取的次數 */ static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter>{ @Override protected 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 */ /** - readLock 獲取記錄容器 ThreadLocal(ThreadLocal 的使用過程中當 HoldCounter.count == 0 時要進行 remove , 不然很有可能導致 內存的泄露) */ private transient ThreadLocalHoldCounter readHolds; /** - 最後一次獲取 readLock 的 HoldCounter 的緩存 - (PS: 還是上面的問題 有了 readHolds 爲什麼還需要 cachedHoldCounter呢? 在非常多的場景中, 這次進行release readLock的線程就是上次 acquire 的線程, 這樣直接通過cachedHoldCounter來進行獲取, 節省了通過 readHolds 的 lookup 的過程) */ private transient HoldCounter cachedHoldCounter; /** - 下面兩個是用來進行記錄 第一次獲取 readLock 的線程的信息 - 準確的說是第一次獲取 readLock 並且 沒有 release 的線程, 一旦線程進行 release readLock, 則 firstReader會被置位 null */ private transient Thread firstReader = null; private transient int firstReaderHoldCount;
- ThreadLocalHoldCounter繼承了ThreadLocal,一個存儲HoldCounter類型對象的ThreadLocal實例,並且重寫了
initialValue()
方法,這意味着,當調用get()方法時,如果之前沒有設置值,將會調用initialValue()
生成value(key爲當前ThreadLocal實例,一個ThreadLocalHoldCounter對象)放入到當前線程的ThreadLocalMap中。 - HoldCounter 用來記錄當前線程獲取讀鎖次數的一個類
- ThreadLocalHoldCounter繼承了ThreadLocal,一個存儲HoldCounter類型對象的ThreadLocal實例,並且重寫了
-
-
ReadLock 讀鎖的實現
-
ReadLock#lock
public void lock() { sync.acquireShared(1); }
同樣是直接調用AQS的final方法
acquireShared()
,直接看定義何時成功獲到共享鎖的方法的具體實現。以非公平鎖爲例。Sync#tryAcquireShared()
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(); //state低16位標識寫鎖,查看是否存在寫鎖 if (exclusiveCount(c) != 0 && //查看當前佔有AQS的線程是否是當前線程 getExclusiveOwnerThread() != current) //如果條件都不滿足,則返回-1,當前線程進入自旋嘗試獲取共享鎖,自旋獲取是則被掛起 return -1; //取高16位讀鎖,查看擁有讀鎖個數 int r = sharedCount(c); //readerShouldBlock()是Sync這個抽象類中的抽象方法。在NonFairSync和FairSync中有不同的實現 ,如果這個方法返回true的話就會導致fullTryAcquireShared的執行 if (!readerShouldBlock() && r < MAX_COUNT && //讀鎖加1,我們看到讀鎖加1會將state+2^16,這就導致了state的高16位代表讀鎖個數 compareAndSetState(c, c + SHARED_UNIT)) { //進入這塊邏輯,會返回1,不會去嘗試自旋獲取共享歐鎖,而是結束方法,線程繼續往下執行 if (r == 0) { firstReader = current; firstReaderHoldCount = 1; } //如果當前線程是重入的,第一次讀的線程就是當前線程 else if (firstReader == current) { firstReaderHoldCount++; } else { // 非 firstReader 讀鎖重入計數更新 HoldCounter rh = cachedHoldCounter; //cachedHoldCounter爲null或者當前線程id不是cachedHoldCounter的線程id if (rh == null || rh.tid != getThreadId(current)) cachedHoldCounter = rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); rh.count++; } return 1; } //第一次獲取讀鎖失敗,有兩種情況: //1)沒有寫鎖被佔用時,嘗試通過一次CAS去獲取鎖時,更新失敗(說明有其他讀鎖在申請) //2)當前線程佔有寫鎖,並且有其他寫鎖在當前線程的下一個節點等待獲取寫鎖,除非當前線程的下一個節點被取消,否則fullTryAcquireShared也獲取不到讀鎖\ //代碼調用 fullTryAcquireShared 大體情況是 AQS 的 sync queue 裏面有其他的節點 或 AQS queue 的 head.next 是個獲取 writeLock 的節點, 或 CAS 操作 state 失敗 return fullTryAcquireShared(current); }
1.如果存在寫鎖,並且持有寫鎖的線程不是當前線程,則返回-1,當前線程進入自旋獲取鎖的過程。
2.如果當前獲取讀鎖的操作不需要被阻塞並且CAS增加讀鎖計數成功且沒有達到讀鎖個數限制,進入記錄當前線程 獲取 readLock 的次數的邏輯,最後返回1,成功獲取到讀鎖。
3.如果前兩種情況都不符合,那麼就執行
fullTryAcquireShared
方法。
2中當前獲取讀鎖的操作是否需要被阻塞時,公平鎖和非公平鎖的實現不同
NonFairSync#readerShouldBlock()
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; }
我們看到NonFairSync類中對是否需要阻塞讀鎖的實現實際上調用的是AQS中的方法,其實就是查看第一個線程Node節點是獨佔類型節點(即是寫鎖Node節點),如果是的話,就需要阻塞讀。
FairSync#readerShouldBlock()
final boolean readerShouldBlock() { return hasQueuedPredecessors(); } public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; //如果隊列中存在線程Node節點, return h != t && //第一個線程Node節點不是當前節點 ((s = h.next) == null || s.thread != Thread.currentThread()); }
如果隊列中存在線程Node節點且點一個線程Node節點不是當前線程,那麼就要阻塞當前讀。
2中記錄當前線程 獲取 readLock 的次數的邏輯分析
if (r == 0) { firstReader = current; firstReaderHoldCount = 1; } //如果當前線程是重入的,第一次讀的線程就是當前線程 else if (firstReader == current) { firstReaderHoldCount++; } else { // 非 firstReader 讀鎖重入計數更新 HoldCounter rh = cachedHoldCounter; //cachedHoldCounter爲null或者當前線程id不是cachedHoldCounter的線程id if (rh == null || rh.tid != getThreadId(current)) cachedHoldCounter = rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); rh.count++; } return 1;
1.如果當前讀鎖計數爲0,那麼將當前線程置爲firstReader線程,firstReaderHoldCount置爲1.
2.如果當前線程是重入的,第一次讀的線程就是當前線程,之前將 firstReaderHoldCount++;
3.否則,找到之前緩存的cachedHoldCounter。
3.1如果緩存的HoldCounter爲空或者緩存HoldCounter不是屬於當前線程的HoldCounter。那麼就將 cachedHoldCounter置爲當前線程的HoldCounter。
3.2 如果緩存的HoldCounter屬於當前線程,如果當前線程的讀鎖計算爲0,那麼就將cachedHoldCounter設置爲當前線程的HoldCounter。這個時候爲0的情況,只可能是釋放共享鎖的方法調用了
readHolds.remove();
將當前線程的ThreadLocalMap中以readHolds爲key的Entry刪除掉,所以這裏需要重置。3.3 3.1或3.2執行完後均將HoldCounter的count+1。
fullTryAcquireShared
/** * Full version of acquire for reads, that handles CAS misses * and reentrant reads not dealt with in tryAcquireShared. */ /** * fullTryAcquireShared 這個方法其實是 tryAcquireShared 的冗餘(redundant)方法, 主要補足 readerShouldBlock 導致的獲取等待 和 CAS 修改 AQS 中 state 值失敗進行的修補工作 */ final int fullTryAcquireShared(Thread current){ /** * This code is 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) // 1. 若此刻 有其他的線程獲取了 writeLock 則當前線程要進入自旋獲取讀鎖的過程,宿命大概就是被掛起了 return -1; // else we hold the exclusive lock; blocking here // would cause deadlock }else if(readerShouldBlock()){ // 2. 判斷 獲取 readLock 的策略 // Make sure we're not acquiring read lock reentrantly if(firstReader == current){ // 3. 若是 readLock 的 重入獲取, 則直接進行下面的 CAS 操作 // 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(); // 4. 若 rh.count == 0 進行 ThreadLocal.remove } } } if(rh.count == 0){ // 5. count != 0 則說明這次是 readLock 獲取鎖的 重入(reentrant), 所以即使出現死鎖, 以前獲取過 readLock 的線程還是能繼續 獲取 readLock return -1; // 6. 進行到這一步只有 當 aqs sync queue 裏面有 獲取 readLock 的node 或 head.next 是獲取 writeLock 的節點 } } } if(sharedCount(c) == MAX_COUNT){ // 7. 是否獲取 鎖溢出 throw new Error("Maximum lock count exceeded"); } if(compareAndSetState(c, c + SHARED_UNIT)){ // 8. CAS 可能會失敗, 但沒事, 我們這邊外圍有個 for loop 來進行保證 操作一定進行 if(sharedCount(c) == 0){ // 9. r == 0 沒有線程獲取 readLock 直接對 firstReader firstReaderHoldCount 進行初始化 firstReader = current; firstReaderHoldCount = 1; }else if(firstReader == current){ // 10. 第一個獲取 readLock 的是 current 線程, 直接計數器加 1 firstReaderHoldCount++; }else{ if(rh == null){ rh = cachedHoldCounter; } if(rh == null || rh.tid != getThreadId(current)){ rh = readHolds.get(); // 11. 還是上面的邏輯, 先從 cachedHoldCounter, 數據不對的話, 再從readHolds拿數據 }else if(rh.count == 0){ readHolds.set(rh); // 12. 爲什麼要 count == 0 時進行 ThreadLocal.set? 因爲上面 tryReleaseShared方法 中當 count == 0 時, 進行了ThreadLocal.remove } rh.count++; cachedHoldCounter = rh; // cache for release // 13. 獲取成功 } return 1; } } }
1.如果寫鎖存在,並且佔有寫鎖的線程不是當前線程,那麼直接返回-1,當前線程將進入自旋獲取讀鎖,宿命大概就是被掛起。
2.否則進行是否需要阻塞讀的判斷
2.1 如果需要阻塞讀
2.1.1如果首次獲取讀鎖的線程是當前線程,不做處理
2.1.2 否則
2.1.2.1 如果rh這個局部變量爲null,那麼將緩存HoldCounter指向該變量。如果緩存HoldCounter所屬的線程不是當前線程,那麼吧rh指向當前線程的HoldCounter,這時,如果rh.count依舊爲0,則remove掉當前線程的HoldCounter。
2.1.2.2 如果rh這個局部變量不爲null,如果rh.count爲0,那麼返回-1.
3.如果之前的操作都沒有導致return退出循環,那麼先判斷讀鎖計數器是否溢出,溢出拋出異常。
4.CAS設置新的讀鎖計數器,通過不斷的循環來保證成功。然後開始增加當前線程 獲取 readLock 的次數,最後返回1,結束獲取讀鎖的方法。
-
ReadLock#unlock
public void unlock() { sync.releaseShared(1); }
也是直接使用了AQS的final方法
releaseShared()
,我們直接看tryReleaseShared()
方法。
protected final boolean tryReleaseShared(int unused) { Thread current = Thread.currentThread(); //1.如果當前線程就是首次獲取讀鎖的線程 if (firstReader == current) { // assert firstReaderHoldCount > 0; //1.1如果當前線程的讀重入次數爲1,將首次獲取讀鎖的線程置爲空 if (firstReaderHoldCount == 1) firstReader = null; //1.2否則只是將讀重入次數減少一次 else firstReaderHoldCount--; } //2.如果不是 else { //獲取上一次獲取讀鎖的線程的HoldCounter HoldCounter rh = cachedHoldCounter; //2.1如果緩存的HoldCounter爲空或者當前線程不是之前緩存的HoldCounter所屬的線程 if (rh == null || rh.tid != getThreadId(current)) //獲取當前線程的HoldCounter 如果之前沒有,側會實例化一個新的 rh = readHolds.get(); int count = rh.count; //2.2當重入次數<=1的時候,我們需要清除掉當前線程的HoldCounter if (count <= 1) { //這裏會remove();防止內存泄漏 readHolds.remove(); if (count <= 0) throw unmatchedUnlockException(); } //重入次數-1 --rh.count; } //3.讀鎖減一 for (;;) { //獲取當前的state int c = getState(); //將讀鎖計數器計數減一 int nextc = c - SHARED_UNIT; //CAS設置新的state值,直到成功才退出循環 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. //判斷是否讀鎖爲0,如果爲0返回true,否則就返回false return nextc == 0; } }
1.對線程的讀鎖重入次數進行減一操作
-
1.1如果當前線程就是首次獲取讀鎖的線程,那麼對firstReader和firstReaderHoldCount進行操作。
1.2如果不是,那麼對cachedHoldCounter進行操作,如果當前線程與之前緩存HoldCounter所屬線程不一
致,需要獲取到屬性當前線程的HoldCounter(如果當前線程沒有HoldCounter,需要實例化一個),然後
進行讀鎖重入次數減一的操作。
2.使用循環CAS完成對讀鎖計數減一,當讀鎖計數爲0的時候,喚醒阻塞的線程。
-
WriteLock寫鎖的實現
-
WriteLock.lock()
public void lock() { sync.acquire(1); }
依舊是調用AQS的final方法
acquire()
方法,我們直接看tryAcquire()
方法的實現。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代表此時有讀鎖或者寫鎖 if (c != 0) { // (Note: if c != 0 and w == 0 then shared count != 0) //如果state不爲0的同時寫鎖計數爲0,意味着當前有讀鎖,需要返回false //或者當前線程並不是佔有寫鎖的線程返回false if (w == 0 || current != getExclusiveOwnerThread()) return false; //如果寫鎖計數大於最大限制,拋出異常 if (w + exclusiveCount(acquires) > MAX_COUNT) throw new Error("Maximum lock count exceeded"); // Reentrant acquire //寫鎖計數加一 ,不用CAS,因爲寫鎖是獨佔鎖 setState(c + acquires); return true; } if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) return false; //如果CAS設置成功,將當前線程設置爲佔有AQS的線程,返回true 爲什麼不需要CAS設置呢? setExclusiveOwnerThread(current); return true; }
1.如果state不爲0,那麼就意味着當前有讀鎖或者寫鎖
1.1如果寫鎖計算爲0,那麼就意味着現在有讀鎖,那麼返回false,進入自旋獲取寫鎖。如果當前線程不是佔有寫鎖的線程,那麼同樣返回false.
1.2判斷是否寫鎖已經飽和了,飽和了拋出異常。
1.3 如果前兩步都沒有導致方法提前返回,那麼就開始講寫鎖計數加一,並且返回true,這裏不需要使用CAS設置,因爲寫鎖是獨佔鎖。
2.如果state爲0,那麼就意味着當前沒有讀寫鎖,但是此時可能有多個嘗試獲取寫鎖的線程執行到這一步,而且這時可能已經有嘗試獲取讀鎖的線程將state改動了,不再爲0了,併發情況下,只要不是原子操作都可能出現問題,所以這個時候我們需要使用CAS來設置,這樣就保證了併發情況下,只有一個線程會被設置爲佔有寫鎖的線程。
-
WriteLock.unlock()
public void unlock() { sync.release(1); } ```
調用AQS的final方法
release()
方法,照例直接看tryRelease()
方法。
protected final boolean tryRelease(int releases) { //1.是否是當前線程持有寫鎖,不是的話,拋出異常 if (!isHeldExclusively()) throw new IllegalMonitorStateException(); int nextc = getState() - releases; //查看寫鎖計數是否爲0,如果爲0就會返回true,接着就會在CLH中存在阻塞線程的時候去喚醒阻塞節點。 boolean free = exclusiveCount(nextc) == 0; if (free) setExclusiveOwnerThread(null); setState(nextc); return free; }
-
總結
本文只是對讀寫鎖進行一個簡單的分析,這個類很有更多更深入的內容,有興趣的可以閱讀源碼進行深入分析。