從ReentrantLock加鎖解鎖角度分析AQS

控球強迫症發表於2020-11-05

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

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

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

從ReentrantLock加鎖解鎖角度分析AQS

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

從ReentrantLock加鎖解鎖角度分析AQS

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

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

(1)ReentrantLock建構函式(為了方便分析,我們以非公平佇列為例子)

從ReentrantLock加鎖解鎖角度分析AQS

(2)lc.lock()

從ReentrantLock加鎖解鎖角度分析AQS

ReentrantLock類中的lock()方法呼叫sync例項中的lock()方法,因為我們是以非公平佇列為例子,所以此時的sync例項的型別為NonfairSync。即呼叫NonfairSync.lock()

從ReentrantLock加鎖解鎖角度分析AQS

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

從ReentrantLock加鎖解鎖角度分析AQS

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

從ReentrantLock加鎖解鎖角度分析AQS

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

從ReentrantLock加鎖解鎖角度分析AQS

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

從ReentrantLock加鎖解鎖角度分析AQS 從ReentrantLock加鎖解鎖角度分析AQS

至此成功獲取了鎖。

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

相關文章