AQS 原理
1. 前言
本節內容主要是對 AQS 原理的講解,之所以需要了解 AQS 原理,是因為后續講解的 ReentrantLock 是基于 AQS 原理的。本節內容相較于其他小節難度上會大一些,基礎薄弱的學習者可以選擇性學習本節內容或者跳過本節內容。
- 了解什么是 AQS,這是認識 AQS 原理的前提,是本節的基礎知識點;
- 了解 AQS 提供的兩種鎖功能,對其有一個全局的了解;
- 了解 AQS 的內部框架原理結構,這是本節課程的核心所在,其他所有的知識點講解都是圍繞這一知識點的;
- 釋放鎖以及添加線程對于 AQS 內部的變化,這是本節課程的重點知識,了解隊列的學習者能夠更快的掌握這部分知識;
- AQS 與 ReentrantLock 的聯系,這是本節課程與 ReentrantLock 之間的過度知識。
2. 什么是 AQS
定義:AbstarctQueuedSynchronizer 簡稱 AQS,是一個用于構建鎖和同步容器的框架。
事實上 concurrent 包內許多類都是基于 AQS 構建的,例如 ReentrantLock,ReentrantReadWriteLock,FutureTask 等。AQS 解決了在實現同步容器時大量的細節問題。
AQS 使用一個 FIFO 隊列表示排隊等待鎖的線程,隊列頭結點稱作 “哨兵節點” 或者 “啞結點”,它不與任何線程關聯。其他的節點與等待線程關聯,每個階段維護一個等待狀態 waitStatus。
3. AQS 提供的兩種功能
從使用層面來說,AQS 的鎖功能分為兩種:獨占鎖和共享鎖。
獨占鎖:每次只能有一個線程持有鎖,比如前面給大家演示的 ReentrantLock 就是以獨占方式實現的互斥鎖;
共享鎖:允許多個線程同時獲取鎖,并發訪問共享資源,比如 ReentrantReadWriteLock。
4. AQS 的內部實現
AQS 的實現依賴內部的同步隊列,也就是 FIFO 的雙向隊列,如果當前線程競爭鎖失敗,那么 AQS 會把當前線程以及等待狀態信息構造成一個 Node 加入到同步隊列中,同時再阻塞該線程。當獲取鎖的線程釋放鎖以后,會從隊列中喚醒一個阻塞的節點 (線程)。
如下圖所示,一個節點表示一個線程,它保存著線程的引用(thread)、狀態(waitStatus)、前驅節點(prev)、后繼節點(next),其實就是個雙端雙向鏈表,其數據結構如下:
Tips:AQS 隊列內部維護的是一個 FIFO 的雙向鏈表,這種結構的特點是每個數據結構都有兩個指針,分別指向直接的后繼節點和直接前驅節點。所以雙向鏈表可以從任意一個節點開始,很方便的訪問前驅和后繼。每個 Node 其實是由線程封裝,當線程爭搶鎖失敗后會封裝成 Node 加入到 ASQ 隊列中去。
5. 添加線程對于 AQS 隊列的變化
當出現鎖競爭以及釋放鎖的時候,AQS 同步隊列中的節點會發生變化,首先看一下添加線程的場景。
這里會涉及到兩個變化:
- 隊列操作的變化:新的線程封裝成 Node 節點追加到同步隊列中,設置 prev 節點以及修改當前節點的前置節點的 next 節點指向自己;
- tail 指向變化:通過同步器將 tail 重新指向新的尾部節點。
6. 釋放鎖移除節點對于 AQS 隊列的變化
第一個 head 節點表示獲取鎖成功的節點,當頭結點在釋放同步狀態時,會喚醒后繼節點,如果后繼節點獲得鎖成功,會把自己設置為頭結點,節點的變化過程如下:
這個過程也是涉及到兩個變化:
head 節點指向:修改 head 節點指向下一個獲得鎖的節點;
新的獲得鎖的節點:如圖所示,第二個節點被 head 指向了,此時將 prev 的指針指向 null,因為它自己本身就是第一個首節點,所以 pre 指向 null。
7. AQS 與 ReentrantLock 的聯系
ReentrantLock 實現:ReentrantLock 是根據 AQS 實現的獨占鎖,提供了兩個構造方法如下:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock 有三個內部類:Sync,NonfairSync,FairSync,繼承關系如下:
總結:我們可以看到,這三個內部類都是基于 AQS 進行的實現,由此可見,ReentrantLock 是基于 AQS 進行的實現。
ReentrantLock 提供兩種類型的鎖:公平鎖,非公平鎖。分別對應 FairSync,NonfairSync。默認實現是 NonFairSync。
8. 小結
本節內容為 AQS 原理進行講解,會涉及到一些原理問題,隊列問題,基礎薄弱的學習者可以跳過或者選看本節內容,不會影響后續課程的學習。本節內容其實主要為了提供原理性的知識,對本節的知識掌握,使我們不僅僅是一個使用者。