ReentrantLock源碼解析
文章目錄
- ReentrantLock源碼解析
- 一、ReentrantLock
- 二、ReentrantLock 的 Sync、FairSync、NonfairSync
- 2.1 Sync、FairSync、NonfairSync
- 2.2 NonfairSync 下的 tryAcquire
- 2.3 FairSync下的 tryAcquire
- 2.4 tryRelease
- 三、lock.lock()
- 3.1 NonfairSync.lock()
- 3.2 FairSync.lock()
- 四、lock.unlock()
- 五、總結
一、ReentrantLock
ReentrantLock
是 Java JUC
中的一個可重入鎖,ReentrantLock鎖
是基于 AQS
實現的。
在 AQS
中,如果需要使用AQS
的特征則需要子類根據使用的場景,重寫下面方法:
//查詢是否正在獨占資源,condition會使用
boolean isHeldExclusively()
//獨占模式,嘗試獲取資源,成功則返回true,失敗則返回false
boolean tryAcquire(int arg)
//獨占模式,嘗試釋放資源,成功則返回true,失敗則返回false
boolean tryRelease(int arg)
//共享模式,嘗試獲取資源,如果返回負數表示失敗,否則表示成功。
int tryAcquireShared(int arg)
//共享模式,嘗試釋放資源,成功則返回true,失敗則返回false。
boolean tryReleaseShared(int arg)
由于這里 ReentrantLock
鎖的特性,所以下面只需關注獨占模式下的幾個方法即可。
關于 AQS
中的方法解析可跳轉查看 AQS源碼解析 這篇文章
下面開始 ReentrantLock
源碼的分析:
二、ReentrantLock 的 Sync、FairSync、NonfairSync
2.1 Sync、FairSync、NonfairSync
在聲明 ReentrantLock
鎖時,有兩種方式,一種是無參構造函數,一種則需要指定一個 fair
參數:
new ReentrantLock();
new ReentrantLock(false);
當使用無參構造函數聲明時,則是創建了一個 NonfairSync
對象:
通過有參的構造函數,則根據傳入的 fair
可以選擇創建一個 FairSync
對象:
其實這里也不難理解 NonfairSync
和 FairSync
其實就是ReentrantLock
鎖中的非公平鎖和公平鎖兩種類型。
點到這兩個類中,可以看到都繼承自 Sync
類:
而 Sync
類,則繼承了 AQS
:
到這里,我們尋找幾個關鍵的方法,在AQS
中獨占模式下,兩大關鍵的方法是交由子類進行實現的,分別是 tryAcquire()
嘗試獲取資源,和 tryRelease()
嘗試釋放資源。
首先來看 tryAcquire()
嘗試獲取資源:
通過 Sync
類的實現源碼發現并沒有重寫 tryAcquire()
方法,那該方法肯定在下面的子類FairSync
和 NonfairSync
,分別看下源碼確實存在重寫的方法:
2.2 NonfairSync 下的 tryAcquire
首先看下 NonfairSync
的 tryAcquire()
實現邏輯,可以看到又調用了 nonfairTryAcquire()
就是 Sync
類中的 nonfairTryAcquire()
,從命名上可以分析出就是非公平鎖的嘗試獲取資源,直觀就是非公平鎖下獲取鎖操作:
進入到 Sync
類中的 nonfairTryAcquire()
中,可以看到首先獲取到 AQS
中的共享資源 state
,如果 state
等于 0
,則將 state
的值修改為 acquires
(默認為1
,下面會分析到),并設置AQS
的獨占線程為當前線程,并返回 true
,說白了不就是獲取到鎖了嗎,那就可以理解為 state
等于 0
即是無鎖的狀態,下面將 state
的值修改為 acquires
就是獲取到鎖了,改變資源的狀態:
接著如果 state
的值不是 0
,則當前鎖已經被別的線程持有了,這里又判斷了下,如果持有鎖的線程正好是當前的線程,那不就是鎖的重入嗎,這種情況下可以直接獲得鎖,不過這里為了記錄重入的次數,對 state
共享資源進行了 + acquires
操作,其實就是 +1
操作。
如果都沒有成功,那此時則獲取鎖失敗,返回 false
2.3 FairSync下的 tryAcquire
在 FairSync
類下的 tryAcquire()
方法中,和前面 NonfairSync
類似,但不同的是,在獲取到鎖時,也就是拿到 state
等于 0
,進行修改資源時,多了步 hasQueuedPredecessors()
的判斷:
下面可以進到 hasQueuedPredecessors()
的方法中,可以看到是由 AQS
提供的方法,主要就是判斷當前節點線程的前面是否還有等待的線程,因為 FairSync
實現的是公平鎖的原則,如果當前線程前面還有等待線程,則獲取鎖資源也輪不到自個,讓前面的老大先來:
hasQueuedPredecessors()
方法理解后,其余的邏輯則和 NonfairSync()
中的一致了。
2.4 tryRelease
到這里已經了解到了tryAcquire()
嘗試獲取資源的邏輯,上面提到了兩個重要方法,還有一個 tryRelease()
沒有分析邏輯,還是首先看 Sync
類中是否有重寫該方法:
通過源碼可以看到,在 Sync
類中就已經對 tryRelease()
進行了重寫,而 NonfairSync
和 FairSync
中都沒有重寫該方法,那釋放資源就是走的 Sync
類下的 tryRelease()
方法:
在該方法中,可以看到首先還是獲取到了 AQS
中的 state
共享資源,然后對該資源進行 - releases
(默認releases
為1
,下面會提到)操作,其實就是 -1
操作:
接著判斷了下,如果當前線程不是持有鎖線程,就拋出異常,也好理解,沒有持有鎖的線程跑過來釋放鎖,那肯定有問題了呀。
接著再進行判斷 state
是不是等于 0
,上面講到在鎖重入的情況下,記錄重入的次數是對 state
進行 +1
操作,而這邊又對 state
進行 -1
操作,如果減到最后 state
有成了最初的 0
,那不就是重入的鎖和當前持有的鎖都釋放完了嗎,這個時候就可以將持有鎖的線程置為空了,并修改最新的 state
:
看到這里就會發現獲取鎖和釋放鎖,無非就是對 AQS
中的共享資源進行操作。理解了這兩大核心的方法后,下面就可以看如何運用在 ReentrantLock
中的了。
三、lock.lock()
在 ReentrantLock
中,需要獲取鎖時,直接使用 lock.lock()
即可,那 lock.lock()
到底做了什么呢,點到該方法中,可以看到是調用的 Sync
的 lock()
方法,而 Sync
中的 lock()
方法是抽象方法,具體實現肯定在子類的 NonfairSync
、 FairSync
中。
3.1 NonfairSync.lock()
首先點進 NonfairSync
非公平鎖中的 lock()
方法,直接進行了將 AQS
中的共享資源 state
由 0
改為 1
,如果修改成功,根據上面分析的結論不就是獲取鎖成功了嗎,可以將 AQS
中的獨占線程設為自己了。但是如果其他線程修改成功了,這里使用 CAS
就會修改失敗,因此就會進到 acquire()
方法,注意這里傳遞的參數默認就是 1
,對應著前面括號中的說明:
而 acquire()
方法,就是 AQS
中的獨占模式獲取同步資源的邏輯,會調用當前方法的 tryAcquire()
嘗試獲取資源,如果獲取不到,則加入到 AQS
的阻塞隊列并阻塞掛起線程。
關于 acquire
方法的源碼解析可查看 AQS源碼解析 。
3.2 FairSync.lock()
在 FairSync
公平鎖中,由于需要遵循先進先出的原則,這里沒有直接已粗暴的形式對 state
進行修改,而是直接調用了 AQS
中的 acquire()
方法,而 acquire()
方法又會調用當前類的 tryAcquire()
獲取資源。
但在當前類的 tryAcquire()
方法中,如果獲取到了資源,會接著進行判斷當前線程的前面是否還有等待的線程,如果有則讓出來讓別人獲取資源,因此就遵循了公平鎖的原則,注意這里傳遞的參數默認就是 1
,同樣對應著前面括號中的說明:
同樣 tryAcquire()
嘗試獲取資源,如果獲取不到,則加入到 AQS
的阻塞隊列并阻塞掛起線程。
四、lock.unlock()
上面了解到了 lock()
的邏輯,既然上鎖了肯定需要解鎖,下面點到 unlock()
方法中,可以看到直接使用了 Sync
的 release()
方法釋放資源,其實是 AQS
中的 release()
方法,注意這里傳遞的參數默認就是 1
,同樣對應著前面括號中的說明:
在 AQS
的 release()
方法中,首先會調用 Sync
類的 tryRelease()
釋放資源,然后對已阻塞的線程進行喚醒:
關于 release()
方法的源碼解析可查看 AQS源碼解析 。
五、總結
通過閱讀 ReentrantLock
的源碼可以發現,大量依賴于 AQS
中提供的方法,所以在閱讀前一定要理解下 AQS
的作用和功能。