[Java原始碼][併發J.U.C]---解析ReentrantRead
整體架構
讀寫鎖在讀執行緒獲得鎖時可以允許多個讀執行緒同時訪問,但是在寫執行緒獲得鎖時,所有的讀執行緒和其他寫執行緒均被阻塞.
ReentrantReadWriteLock.png
從上圖可以看到
1.
ReentrantReadWriteLock
主要是實現了介面ReadWriteLock
來實現讀寫鎖,並且真正返回的讀鎖是ReadLock
的一個例項,寫鎖是WriteLock
的一個例項.
2.ReentrantReadWriteLock
中有一個Sync
的成員變數sync
(Sync
是ReentrantReadWriteLock
內部類), 並且ReadLock
和WriteLock
使用了該成員變數sync
來實現它們從介面Lock
繼承的抽象方法.
3.Sync
的一個例項sync
可以是一個FairSync
或者NonfairSync
, 並且Sync
類繼承了AbstractQueuedSynchronizer
,由此可知Sync
是ReentrantReadWriteLock
類的核心,並且實現了讀寫鎖的具體邏輯.
接下來分析的內容都是跟
Sync
類息息相關.
讀寫狀態的設計
先了解一下讀寫狀態的設計. 我們知道
AQS
中有一個狀態值, 比如在ReentrantLock
中表示持有鎖的執行緒重入了多少次. 但是在ReentrantReadWriteLock
中有讀鎖和寫鎖因此需要劃分,所以高16
位代表讀鎖的狀態,低16
位代表寫鎖的狀態.
如圖所示,一個執行緒已經獲取了寫鎖,並且重進入了兩次,同時也連續獲取了兩次讀鎖.(有人可能會疑惑為什麼在獲得寫鎖的同時還可以獲得讀鎖呢, 在鎖降級的時候你會得到答案.)
來自Java併發程式設計的藝術.png
從讀寫鎖的作用可知讀鎖是一個共享鎖, 寫鎖是一個互斥鎖. 因此
sharedCount(int c)
是為了獲取讀鎖的狀態值,exclusiveCount(int c)
是為了獲取寫鎖的狀態值.
abstract static class Sync extends AbstractQueuedSynchronizer { 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; /** 返回c的高16位 讀狀態*/ static int sharedCount(int c) { return c >>> SHARED_SHIFT; } /** 返回c的低16位 寫狀態*/ static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } }
寫鎖的獲取和釋放
先分析寫鎖是因為寫鎖與之前分析的鎖在獲取和釋放的過程基本類似,而讀鎖相較於寫鎖會稍微複雜一點點.
寫鎖的獲取
原始碼如下
作用: 當前執行緒嘗試獲取寫鎖, 獲取成功返回true
,獲取失敗返回false
.
/** * 作用: 寫鎖的獲取 * * @param acquires 獲取的個數 * @return true表示獲取鎖, false表示未獲取鎖 */ protected final boolean tryAcquire(int acquires) { Thread current = Thread.currentThread(); int c = getState(); // 整體狀態 int w = exclusiveCount(c); // 寫狀態的個數 /** * 整體狀態如果等於0 表明讀鎖和寫鎖目前都沒有執行緒獲取到 則可以去獲取寫鎖 * 如果不等於0 * 1. 存在讀鎖或者當前執行緒不是已經獲取寫鎖的執行緒,則直接返回 * 2. 如果寫鎖的數量沒有超過最高值則獲得寫鎖 */ if (c != 0) { // (Note: if c != 0 and w == 0 then shared count != 0) // 存在讀鎖或者當前執行緒不是已經獲取寫鎖的執行緒 if (w == 0 || current != getExclusiveOwnerThread()) return false; if (w + exclusiveCount(acquires) > MAX_COUNT) throw new Error("Maximum lock count exceeded"); // 重入式獲取 setState(c + acquires); return true; } /** * 表示整體狀態為0 * 如果writeShouldBlock需要阻塞或者CAS操作不成功則返回false */ if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) return false; /** * 請注意setExclusiveOwnerThread該方法設定的是寫鎖 */ setExclusiveOwnerThread(current); // 設定當前執行緒是獲得寫鎖的執行緒 return true; }
其實獲取寫鎖的邏輯比較簡單. 具體細節可以參考上面的註解.
1. 如果不存在讀鎖或寫鎖(狀態為0
),則成功獲取鎖並設定setExclusiveOwnerThread
後返回true
.
2. 如果存在讀鎖,則直接返回false
.
3. 如果存在寫鎖, 分以下兩種情況:
3.1 如果寫鎖的執行緒不是當前執行緒,則返回
false
.3.2 如果寫鎖的重入數已經超過了最大值,則返回
false
.3.3 設定寫鎖的重入數(加
1
), 返回true
.
流程圖如下:
tryAcquire.png
關於writeShouldBlock()
和readShouldBlock()
這兩個方法是
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(); } } static final class FairSync extends Sync { private static final long serialVersionUID = -2274990926593161451L; final boolean writerShouldBlock() { return hasQueuedPredecessors(); } final boolean readerShouldBlock() { return hasQueuedPredecessors(); } // 如果前面有節點 返回true 說明需要阻塞 }
寫鎖的釋放
作用: 寫鎖的釋放
/** * * 作用: 寫鎖的釋放 * @param releases 釋放的個數 * @return 寫鎖是否完全釋放 true 完全釋放 */ 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; }
例子: 測試寫鎖的獲取和釋放
工具類
SleepUnit
和Cache
package com.sourcecode.reentrantreadwritelock;import java.util.HashMap;import java.util.Map;public class Cache { static Map<String, Object> map = new HashMap<String, Object>(); static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); static Lock r = rwl.readLock(); static Lock w = rwl.writeLock(); // 獲取一個key對應的value public static final Object get(String key) { r.lock(); try { System.out.println(Thread.currentThread().getName() + " gets lock."); SleepUnit.sleep(5); return map.get(key); } finally { System.out.println(Thread.currentThread().getName() + " releases lock."); r.unlock(); } } // 設定key對應的value,並返回舊的value public static final Object put(String key, Object value) { w.lock(); try { System.out.println(Thread.currentThread().getName() + " gets lock."); SleepUnit.sleep(10); return map.put(key, value); } finally { System.out.println(Thread.currentThread().getName() + " releases lock."); w.unlock(); } } // 清空所有的內容 public static final void clear() { w.lock(); try { map.clear(); } finally { w.unlock(); } } }
public class SleepUnit { public static void sleep(int time) { try { TimeUnit.SECONDS.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } } }
測試類. 啟動五個執行緒去獲取寫鎖,透過列印出
AQS
中的等待佇列來觀察情況.
public class TestCache { public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(new Runner(), "thread-" + i).start(); } for (int i = 0; i < 10; i++) { SleepUnit.sleep(5); Cache.rwl.printWaitingNode(); } } static class Runner implements Runnable { public void run() { Cache.put("k0", "k0"); } } }
結果輸出: 結果並沒有什麼意外, 當所有執行緒嘗試去獲取寫鎖時只有一個執行緒可以拿到鎖.
thread-0 gets lock.[NULL,-1]->[thread-3,獨佔,-1]->[thread-2,獨佔,-1]->[thread-1,獨佔,-1]->[thread-4,獨佔,0]->[NULL,-1]->[thread-3,獨佔,-1]->[thread-2,獨佔,-1]->[thread-1,獨佔,-1]->[thread-4,獨佔,0]->thread-3 gets lock.[NULL,-1]->[thread-2,獨佔,-1]->[thread-1,獨佔,-1]->[thread-4,獨佔,0]->[NULL,0]->thread-2 gets lock.[thread-2,獨佔,-1]->[thread-1,獨佔,-1]->[thread-4,獨佔,0]->[NULL,-1]->[thread-1,獨佔,-1]->[thread-4,獨佔,0]->thread-1 gets lock.[NULL,-1]->[thread-4,獨佔,0]->[NULL,-1]->[thread-4,獨佔,0]->thread-4 gets lock.[NULL,0]->[NULL,0]->[NULL,0]->
讀鎖的獲取和釋放
讀鎖相對於寫鎖來說比較複雜點,因為讀鎖的時候是共享的,意味著每個執行緒都可以獲得執行緒, 又因為讀鎖是可重入的,所以每個獲得讀鎖的執行緒都有一個對應的可重入的數量.說到這裡很容易就會想到用
ThreadLocal
類來實現的. 關於ThreadLocal
可以參考我的另外一篇部落格, 因此接下來先介紹一下讀鎖用到的一些變數及其作用.
讀鎖使用到的變數介紹
/** * 一個ThreadLocal的子類,value值是HoldCounter類的物件並重寫了initValue()方法 */ static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> { public HoldCounter initialValue() { return new HoldCounter(); } } /** * 一個thredlocal例項儲存執行緒對應的HoldCount * 在建構函式或者readObject中完成初始化 * 當讀鎖執行緒的重入數變為0時,會被removed. */ private transient ThreadLocalHoldCounter readHolds; /** * 成功獲取讀鎖的最後一個執行緒的HoldCounter物件. * 為了避免總是去readHolds中查詢 */ private transient HoldCounter cachedHoldCounter; /** * firstReader是第一個獲得讀鎖定的執行緒, * 嚴格意義上是第一個使得讀鎖狀態值從0變為1的執行緒 * firstReaderHoldCount是其對應的重入數 * */ private transient Thread firstReader = null; private transient int firstReaderHoldCount; /** * 建構函式, 初始化readHolds並設定狀態 */ Sync() { readHolds = new ThreadLocalHoldCounter(); setState(getState()); // ensures visibility of readHolds }
在讀鎖中主要使用了三個變數來保持讀鎖的獲取和釋放.
1.firstReader
儲存著第一個把整體狀態從0
變為1
的執行緒.
2.cachedHoldCounter
儲存著最後一個獲取讀鎖執行緒的HoldCounter
物件.
3.readHolds
儲存每個執行緒和其對應的HoldCounter
物件, 不包括firstReader
, 包括最後一個獲取讀鎖執行緒.
讀鎖的獲取
tryAcquireShared
方法.
/** * @param unused 釋放 * @return 返回一個數值如果大於等於0,表明獲得鎖. * 返回一個數值如果小於0,表明沒有獲得鎖. */ protected final int tryAcquireShared(int unused) { Thread current = Thread.currentThread(); int c = getState(); //獲取當前狀態 if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) //如果寫鎖存在並且寫鎖持有者不是當前執行緒 return -1; // 說明 1.寫鎖不存在 或者 2.寫鎖存在但是寫鎖持有者是當前執行緒 int r = sharedCount(c); // 獲取讀鎖的個數 /** * 1. 讀鎖不需要阻塞. * 2. 讀鎖的總個數沒有超過最大數. * 3. 透過CAS設定c的狀態 因為高16位是讀鎖的個數 所以需要加上1<<16. */ if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) { /** * 上面三個條件都滿足的情況下會進入這裡繼續執行 * 1. r == 0 意味著當前執行緒是第一個獲得讀鎖的執行緒(之前沒有獲得過). * 2. firstReader == current 意味當前執行緒是那個之前第一個獲得讀鎖的執行緒 可以重入 * 3. 如果都不是就說明當前執行緒不是第一個獲得讀鎖的執行緒,因此當前執行緒最起碼是第二個獲得讀鎖的執行緒, * a. 先去cachedHoldCounter看一下是不是最後一次獲得讀鎖的執行緒,如果不是就把當前執行緒快取起來 * (因為此時該執行緒是目前最後一個獲得讀鎖的執行緒) * b. 如果是的話如果rh.count==0,就需要把從readHolds中新增進去 * (這是因為在對應的release中rh.count==0的時候readHolds做了清除操作) * rh.count++ * 返回1,代表成功獲得鎖. */ 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 1; } return fullTryAcquireShared(current); }
作用: 嘗試獲取讀鎖, 返回大於等於
0
的數表明獲得鎖, 返回小於0
的數表明未獲得鎖.
邏輯如下:
1. 如果另一個執行緒持有寫鎖定,則失敗.
2. 如果進入到這裡表明兩種情況寫鎖不存在或者寫鎖存在但是寫鎖持有者就是當前執行緒
3. 如果同時滿足3個條件分別是a.讀鎖不需要阻塞和b.讀鎖的總個數沒有超過最大數和c.CAS設定狀態成功, 則當前執行緒成功獲得讀鎖. 否則進入fullTryAcquireShared
方法(放到後面分析).
4. 滿足了上面3
個條件後需要更新讀鎖的相關變數.基本邏輯如下:
4.1 如果讀鎖狀態值為
0
,表明該執行緒是第一個獲得讀鎖的執行緒,設定firstReader
和firstReadHoldCount
變數.4.2 如果不是4.1則表明讀鎖已經被執行緒獲取過了,那麼如果當前執行緒就是那個第一次獲得讀鎖的執行緒,則設定其重入數
firstReaderHoldCount
即可.4.3 如果不是4.2則表明讀鎖已經被獲取過了並且當前執行緒並不是那個第一次獲得讀鎖的執行緒,此時就可以去快取
cachedHoldCounter
中看看是不是當前執行緒,如果不是的話就從readHolds
中獲取並將其快取在cachedHoldCounter
中. 最後rh.count++
設定一下重入數.
注意 當某個HoldCounter
的count
為0的時候,readHolds
是會將其清除掉的.
流程圖如下:
tryAcquireShared.png
fullTryAcquireShared
方法
/** * 作用: 獲取鎖, 返回值大於等於0表示 * 用於處理tryAcquireShared方法中未能滿足的3個條件 */ final int fullTryAcquireShared(Thread current) { HoldCounter rh = null; for (;;) { int c = getState(); // 獲取當前狀態 if (exclusiveCount(c) != 0) { // 如果寫鎖不為0 表明存在寫鎖 // 如果寫鎖不是當前執行緒(說明此刻已經有別的執行緒獲得寫鎖了),則需要阻塞當前執行緒所以返回-1. if (getExclusiveOwnerThread() != current) return -1; // else we hold the exclusive lock; blocking here // would cause deadlock. /** * 這一段話的意思是如果當前執行緒如果在這裡block了,那會形成死鎖, * 因為當前執行緒已經在持有寫鎖的情況來請求讀鎖的,那麼該鎖在沒有釋放鎖的情況下block了 * 就會形成死鎖了 */ } else if (readerShouldBlock()) { // 不存在寫鎖並且需要阻塞 // Make sure we're not acquiring read lock reentrantly /** * 確認一下當前執行緒有沒有在之前獲得鎖,也就是在阻塞前確認一下不是重入讀鎖的執行緒 * 如果是重入鎖的話就讓他操作CAS 如果不是的話就需要阻塞 * 至於為什麼,我個人理解如下: * 對公平鎖來說,readShouldBlock()返回true,表明AQS佇列中有等待寫鎖的執行緒, * 那麼如果重入讀鎖也返回-1讓其阻塞的話那就會形成死鎖,因為該重入讀鎖由於阻塞無法釋放讀鎖, * AQS等待佇列中的寫鎖又因為讀鎖的存在而無法獲得寫鎖從而形成死鎖了. */ 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) // 計數為0, 需要從readHolds中刪除 readHolds.remove(); } } if (rh.count == 0) //說明當前執行緒之前沒有獲得鎖 return -1; } } // 如果讀鎖的個數達到最大值丟擲error if (sharedCount(c) == MAX_COUNT) throw new Error("Maximum lock count exceeded"); // CAS操作 邏輯跟tryAcquireShared方法裡面的類似. 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; } } }
fullTryAcquireShared
是為了處理tryAcquireShared
中無法同時滿足那三個條件而進行的處理方法,for
迴圈操作.
1. 如果存在寫鎖並且寫鎖不是當前執行緒則需要阻塞當前執行緒返回-1.
2. 如果存在寫鎖並且寫鎖就是當前執行緒則不需要管readerShouldBlock()
方法進行CAS
操作.
3. 如果不存在寫鎖,如果當前執行緒之前獲得過讀鎖則進行CAS
操作,否則返回-1,阻塞當前執行緒.
4. 如果讀鎖的個數達到最大值則丟擲Error
.
5. 進行CAS
操作.
對於第3點的個人理解
對公平鎖來說,readShouldBlock()返回true,表明AQS佇列中有等待寫鎖的執行緒,那麼如果重入讀鎖也返回-1讓其阻塞的話那就會形成死鎖,因為該重入讀鎖由於阻塞無法釋放讀鎖,AQS等待佇列中的寫鎖又因為讀鎖的存在而無法獲得寫鎖從而形成死鎖了.
對應流程圖如下:
fullTryAcquireShared.png
讀鎖的釋放
/** * 作用: 釋放讀鎖 * @param unused * @return */ protected final boolean tryReleaseShared(int unused) { Thread current = Thread.currentThread(); // 獲取當前執行緒 /** * 1. firstReader == current 表明當前執行緒是那個第一個獲得讀鎖的執行緒,可以直接操作firstReaderHolderCount就可以了 * 2. 如果不是則看是不是最後一次獲得讀鎖的執行緒, * a. 如果不是則取出當前執行緒對應的holdcount,儲存到rh中 * b. 如果是直接儲存到rh * * 如果rh的count為1,表明當前執行緒獲得讀鎖後沒有重入過,既然是釋放鎖,這個時候就需要從threadlocal中刪除掉 * * rh.count-- * */ if (firstReader == current) { // assert firstReaderHoldCount > 0; if (firstReaderHoldCount == 1) firstReader = null; else firstReaderHoldCount--; } else { HoldCounter rh = cachedHoldCounter; // 不是最後一個獲得讀鎖的執行緒,需要從threadlocal中也就是readHolds中取出當前執行緒的HoldCount if (rh == null || rh.tid != getThreadId(current)) rh = readHolds.get(); int count = rh.count; // count <= 1 需要從readHolds中刪除, 進一步如果count<=0表明錯誤 if (count <= 1) { readHolds.remove(); if (count <= 0) throw unmatchedUnlockException(); } // 無論怎麼樣 rh已經是當前執行緒對應的HoldCount, 釋放一個就是減少一個 --rh.count; } // 迴圈操作更新狀態, 如果讀鎖的個數為0,則表明所有讀鎖都釋放完畢這個時候返回true. // 不然其他情況都是返回false. for (;;) { int c = getState(); int nextc = c - SHARED_UNIT; if (compareAndSetState(c, nextc)) return nextc == 0; } }
作用: 釋放讀鎖,返回true如果所有的讀鎖釋放完,否則返回false.
邏輯很簡單,可以直接看程式碼註解.
鎖降級
鎖降級: 鎖降級指的是寫鎖降級成為讀鎖。如果當前執行緒擁有寫鎖,然後將其釋放,最後再獲取讀鎖,這種分段完成的過程不能稱之為鎖降級.鎖降級是指把持住(當前擁有的)寫鎖,再獲取到讀鎖,隨後釋放(先前擁有的)寫鎖的過程.
其實讀鎖這部分已經分析到了鎖降級的內容了, 在上面的分析中我們已經看到在當前執行緒獲取讀鎖的過程中如果存在寫鎖並且該寫鎖就是當前執行緒的時候可以去獲得讀鎖.
必要性: 主要是為了保證資料的可見性,如果當前執行緒不獲取讀鎖而是直接釋放寫鎖(接著不加鎖直接讀取資料),假設此刻另一個執行緒(記作執行緒T)獲取了寫鎖並修改了資料,那麼當前執行緒無法感知執行緒T的資料更新. 如果當前執行緒獲取讀鎖,即遵循鎖降級的步驟,則執行緒T將會被阻塞,直到當前執行緒使用資料並釋放讀鎖之後,執行緒T才能獲取寫鎖進行資料更新。
Sync
類中一些其餘的方法
/** * 嘗試獲取寫鎖,該方法給tryLock呼叫,返回false該執行緒也不會阻塞 */ 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; } /** * 嘗試獲取讀鎖,該方法給tryLock呼叫,返回false該執行緒也不會阻塞 */ 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; } } }
作者:nicktming
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1762/viewspace-2816315/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java併發之Semaphore原始碼解析(一)Java原始碼
- Java併發之Semaphore原始碼解析(二)Java原始碼
- Java併發之ReentrantLock原始碼解析(一)JavaReentrantLock原始碼
- Java併發之ReentrantLock原始碼解析(二)JavaReentrantLock原始碼
- Java併發之ReentrantLock原始碼解析(三)JavaReentrantLock原始碼
- Java併發之ReentrantLock原始碼解析(四)JavaReentrantLock原始碼
- Java併發之ThreadPoolExecutor原始碼解析(二)Javathread原始碼
- Java併發之ThreadPoolExecutor原始碼解析(三)Javathread原始碼
- Java併發包原始碼學習系列:同步元件CountDownLatch原始碼解析Java原始碼元件CountDownLatch
- Java併發包原始碼學習系列:同步元件CyclicBarrier原始碼解析Java原始碼元件
- Java併發包原始碼學習系列:同步元件Semaphore原始碼解析Java原始碼元件
- Java併發之ReentrantReadWriteLock原始碼解析(一)Java原始碼
- Java併發之ReentrantReadWriteLock原始碼解析(二)Java原始碼
- Java併發集合類ConcurrentHashMap底層核心原始碼解析JavaHashMap原始碼
- java併發之hashmap原始碼JavaHashMap原始碼
- Java併發包原始碼學習系列:基於CAS非阻塞併發佇列ConcurrentLinkedQueue原始碼解析Java原始碼佇列
- Java併發程式設計,深度探索J.U.C - AQSJava程式設計AQS
- Java併發包原始碼學習系列:執行緒池ThreadPoolExecutor原始碼解析Java原始碼執行緒thread
- Java併發包原始碼學習系列:執行緒池ScheduledThreadPoolExecutor原始碼解析Java原始碼執行緒thread
- Java併發包原始碼學習系列:JDK1.8的ConcurrentHashMap原始碼解析Java原始碼JDKHashMap
- Java併發包原始碼學習系列:阻塞佇列實現之SynchronousQueue原始碼解析Java原始碼佇列
- Java併發包原始碼學習系列:阻塞佇列實現之DelayQueue原始碼解析Java原始碼佇列
- 併發程式設計之:AQS原始碼解析程式設計AQS原始碼
- Java併發包原始碼學習系列:阻塞佇列實現之LinkedTransferQueue原始碼解析Java原始碼佇列
- Java併發包原始碼學習系列:阻塞佇列實現之ArrayBlockingQueue原始碼解析Java原始碼佇列BloC
- Java併發包原始碼學習系列:阻塞佇列實現之PriorityBlockingQueue原始碼解析Java原始碼佇列BloC
- 併發容器J.U.C -- AQS元件(一)AQS元件
- Java併發之AQS原始碼分析(二)JavaAQS原始碼
- Java併發包原始碼學習系列:阻塞佇列實現之LinkedBlockingDeque原始碼解析Java原始碼佇列BloC
- Java併發包原始碼學習系列:阻塞佇列實現之LinkedBlockingQueue原始碼解析Java原始碼佇列BloC
- Java併發包原始碼學習系列:ReentrantReadWriteLock讀寫鎖解析Java原始碼
- 併發容器J.U.C -- AQS同步元件(二)AQS元件
- java 併發程式設計-AQS原始碼分析Java程式設計AQS原始碼
- Java——HashMap原始碼解析JavaHashMap原始碼
- Java——ArrayList原始碼解析Java原始碼
- Java WeakHashMap 原始碼解析JavaHashMap原始碼
- Java TreeMap 原始碼解析Java原始碼
- Java 併發程式設計(十五) -- Semaphore原始碼分析Java程式設計原始碼