從ReentrantLock加鎖解鎖角度分析AQS

  • 2020 年 11 月 5 日
  • 筆記

本文用於記錄在學習AQS時,以ReentrantLock為切入點,深入源碼分析ReentrantLock的加鎖和解鎖過程。

同步器AQS的主要使用方式是繼承,子類通過繼承同步器並實現它的抽象方法來管理同步狀態(通常鎖或者同步組件內部會實現一個Sync類(該類是一個靜態內部類),然後讓Sync類去繼承AQS類,通過繼承AQS隊列同步器並實現它的抽象方法來管理同步狀態),對同步狀態進行更改需要使用同步器提供的3個方法 getStatesetStatecompareAndSetState ,它們保證狀態改變是安全的。

下圖是ReentrantLock中所有的內部類和方法:

3個內部類之間的關係如下圖所示:

以ReentrantLock為例,從源碼分析如何進行加鎖和解鎖。

ReentrantLock lc = new ReentrantLock();
lc.lock();
lc.unlock();

(1)ReentrantLock構造函數(為了方便分析,我們以非公平隊列為例子)

(2)lc.lock()

ReentrantLock類中的lock()方法調用sync實例中的lock()方法,因為我們是以非公平隊列為例子,所以此時的sync實例的類型為NonfairSync。即調用NonfairSync.lock()

緊接着進入209行對應的acquire(1)方法:該方法位於AQS框架中,且被final修飾,即不能被子類重寫。

緊接着進入tryAcquire(int arg)方法

我們發現AQS中的tryAcquire(int arg)方法中沒有實現體,說明tryAcquire(int arg)方法需要AQS子類實現,根據上面的繼承圖可知,Sync以及NonfairSync都是AQS的子類,由下圖可知,tryAcquire(int arg)方法的實現位於NonfairSync內部類中

進入NonfairSync內部類中的ryAcquire(int arg)方法可以發現其調用了Sync靜態內部類中的nonfairTryAcquire方法。


至此成功獲取了鎖。

(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()方法。