本文用於記錄在學習AQS時,以ReentrantLock為切入點,深入原始碼分析ReentrantLock的加鎖和解鎖過程。
同步器AQS的主要使用方式是繼承,子類通過繼承同步器並實現它的抽象方法來管理同步狀態(通常鎖或者同步元件內部會實現一個Sync類(該類是一個靜態內部類),然後讓Sync類去繼承AQS類,通過繼承AQS佇列同步器並實現它的抽象方法來管理同步狀態),對同步狀態進行更改需要使用同步器提供的3個方法 getState
、setState
和 compareAndSetState
,它們保證狀態改變是安全的。
下圖是ReentrantLock中所有的內部類和方法:
![從ReentrantLock加鎖解鎖角度分析AQS](https://i.iter01.com/images/ca58e8371a5f73238907d6bfb4a8f9881edbb821ea0c5566f152d7b2692235cb.png)
3個內部類之間的關係如下圖所示:
![從ReentrantLock加鎖解鎖角度分析AQS](https://i.iter01.com/images/8b2e5981f8a2a38078efcf78ea70a1decdc02c5879b908cf3cdcd1302db0432d.png)
以ReentrantLock為例,從原始碼分析如何進行加鎖和解鎖。
ReentrantLock lc = new ReentrantLock();
lc.lock();
lc.unlock();
(1)ReentrantLock建構函式(為了方便分析,我們以非公平佇列為例子)
![從ReentrantLock加鎖解鎖角度分析AQS](https://i.iter01.com/images/668e89ca30d330b2393dbcf099780422889bc79858c3dc0cc45a25727b6f1a92.png)
(2)lc.lock()
![從ReentrantLock加鎖解鎖角度分析AQS](https://i.iter01.com/images/8688925433438bf03623468532b36ccb2c6b6859a88864db70b3d00510cc80a9.png)
ReentrantLock類中的lock()
方法呼叫sync例項中的lock()方法,因為我們是以非公平佇列為例子,所以此時的sync例項的型別為NonfairSync。即呼叫NonfairSync.lock()
![從ReentrantLock加鎖解鎖角度分析AQS](https://i.iter01.com/images/dc67a07683ceb349589c0e0a9804cbdb28a7145b4a39b00d03f73299d1578df3.png)
緊接著進入209行對應的acquire(1)方法:該方法位於AQS框架中,且被final修飾,即不能被子類重寫。
![從ReentrantLock加鎖解鎖角度分析AQS](https://i.iter01.com/images/1a4ff1fc696f98d806c2fda6cda0f5ab33a5717151375e3e336f8152521d5ba6.png)
緊接著進入tryAcquire(int arg)方法
![從ReentrantLock加鎖解鎖角度分析AQS](https://i.iter01.com/images/faad5bcb21cf1492f0eb6d4b8d52e0ed37c5755f772e93d280940fae8f1207e7.png)
我們發現AQS中的tryAcquire(int arg)方法中沒有實現體,說明tryAcquire(int arg)方法需要AQS子類實現,根據上面的繼承圖可知,Sync以及NonfairSync都是AQS的子類,由下圖可知,tryAcquire(int arg)方法的實現位於NonfairSync內部類中
![從ReentrantLock加鎖解鎖角度分析AQS](https://i.iter01.com/images/da6d1070d0139060c50075ce23e9d54ec48943bc08f94061e461d1531c555fa3.png)
進入NonfairSync內部類中的ryAcquire(int arg)方法可以發現其呼叫了Sync靜態內部類中的nonfairTryAcquire方法。
![從ReentrantLock加鎖解鎖角度分析AQS](https://i.iter01.com/images/f7106429a3dc5b00cc6c8ef2a4c779f375eac3f1eb7989be1812640e66382e66.png)
![從ReentrantLock加鎖解鎖角度分析AQS](https://i.iter01.com/images/c76cfe952ce4167706107ebc6caee8de3f5e456b5866bdda6fef6840aa21bc6c.png)
至此成功獲取了鎖。
(1) 通過ReentrantLock的加鎖方法Lock進行加鎖操作。
(2) 會呼叫到內部類Sync的Lock方法,根據ReentrantLock初始化選擇的公平鎖和非公平鎖,執行相關內部類的Lock方法,本質上都會執行AQS的acquire方法。
(3) AQS的Acquire方法會執行tryAcquire方法,但是由於tryAcquire需要自定義同步器實現,因此執行了ReentrantLock中的tryAcquire方法,由於ReentrantLock是通過公平鎖和非公平鎖內部類實現的tryAcquire方法,因此會根據鎖型別不同,執行不同的tryAcquire。
(4) tryAcquire是獲取鎖邏輯,獲取失敗後,會執行框架AQS的後續邏輯,跟ReentrantLock自定義同步器無關。
(3)lc.unlock()
釋放鎖的過程和加鎖過程類似,不走進原始碼,直接上文字版流程
(1) 通過ReentrantLock的解鎖方法Unlock進行解鎖。
(2) Unlock會呼叫內部類Sync的release方法,該方法繼承於AQS。
(3) release中會呼叫tryRelease方法,tryRelease需要自定義同步器實現,tryRelease只在ReentrantLock中的Sync實現,因此可以看出,釋放鎖的過程,並不區分是否為公平鎖。
(4) 釋放成功後,所有處理由AQS框架完成,與自定義同步器無關。
經過上述的學習,可以歸納總結出如果要自定義一個同步元件,可以按照如下步驟
- 第一步:內部寫一個Sync類繼承
AbstractQueuedSynchronizer
介面。 - 第二步:根據是否獨佔來重寫佇列同步器中的方法,如果需要獨佔則實現
tryAcquire()/tryRelease()
等方法,如果不需要獨佔,則實現tryAcquireShared(int acquires)和tryReleaseShared(int releases)
等方法。 - 第三步:初始化Sync物件,並在鎖的獲取/釋放方法(通常為lock()以及unlock()方法)中呼叫AQS的
acquire()/release()
或者acquireShared()/releaseShared()
方法。