AQS介紹
AQS的含義
AQS是java.util.concurrent.locks.AbstractQueuedSynchronizer的簡稱,直譯就是“抽象隊列同步器”,它是java中大部分lock類的間接實現者。AQS中實現的各種邏輯非常精妙,在此膜拜一下Doug Lea老爺子。
剛剛有提到AQS間接實現了lock類,檢查java中提供的lock類,如ReentrantLock,ReentrantReadWriteLock,StampedLock,CountDownLatch,CyclicBarrier等,內部都有AQS的實現類,完成了不同邏輯來承載不同Lock的實現。
AQS的構成
查看源碼可知,大致有如下內容:
- static final class Node {...} // 鏈表節點
- public class ConditionObject implements Condition, java.io.Serializable {...} // 條件對象
- private transient volatile Node head; // 鏈表頭
- private transient volatile Node tail; // 鏈表尾
- private volatile int state; // 資源
- private static final Unsafe unsafe = Unsafe.getUnsafe();// 用於cas操作的變量
- private static final long stateOffset,headOffset,tailOffset,waitStatusOffset,nextOffset; // 用於cas操作的變量
- 一些實例方法操作鏈表及資源
- 一些抽象方法等待被實現
AQS的由來
在AQS的源碼中,存在一個由head和tail構成的鏈表,在知曉它存在的作用前,先了解一下硬件領域的多核CPU架構。
從系統架構來看,目前的商用服務器大體可以分爲三類,即對稱多處理器結構 (SMP : Symmetric Multi-Processor) ,非一致存儲訪問結構 (NUMA : Non-Uniform Memory Access) ,以及海量並行處理結構 (MPP : Massive Parallel Processing) 。
處理器架構
SMP架構
即對稱多處理器結構,在這種架構中,一臺計算機由多個CPU組成,並共享內存和其他資源,所有的CPU都可以平等的訪問內存、I/O等。雖然可以同時使用多個CPU,但從外部表現來看,它們就如同一臺單CPU機器一樣,操作系統將任務隊列對稱地分佈於多個CPU之上,從而極大地提高了整個系統的數據處理能力。
日常的pc機,筆記本,手機還有一些老的服務器都是這個架構,其架構簡單,但是拓展性能非常差,從linux 上也能看到:
ls /sys/devices/system/node/# 如果只看到一個node0 那就是smp架構
但是隨着CPU數量的增加,每個CPU都要訪問共享資源,而資源在某些場景下只能單線程訪問,在某些場景下的操作又必須通知到其他CPU,那麼這就帶來了性能損耗、資源浪費,成爲了系統瓶頸。
NUMA架構
即非一致存儲訪問,這種模型的是爲了解決smp擴容性很差而提出的技術方案。它按組將CPU分爲多模塊,每個CPU模塊由多個CPU組成,並且具有獨立的本地內存、I/O等,模塊之間的訪問通過互聯模塊完成(類似遠程通信),訪問本地資源的速度會遠高於訪問外部資源。
NUMA架構相當於打包多個SMP架構的CPU,它能較好解決SMP架構存在的擴展問題;但是,在NUMA的單個CPU模塊中,雖然控制了CPU數量減少了共享資源的操作時的性能損耗,由於存在互聯模塊的工作,在CPU模塊增加時,並不能線性的增加系統性能。
MPP架構
MPP 提供了另外一種進行系統擴展的方式,它由多個 SMP 服務器通過一定的節點互聯網絡進行連接,協同工作,完成相同的任務,從用戶的角度來看是一個服務器系統。 其基本特徵是由多個 SMP 服務器(每個 SMP 服務器稱節點)通過節點互聯網絡連接而成,每個節點只訪問自己的本地資源(內存、存儲等),是一種完全無共享(Share Nothing)結構,因而擴展能力最好,理論上其擴展無限制,目前的技術可實現512個節點互聯,數千個 CPU。 實驗證明, SMP 服務器 CPU 利用率最好的情況是 2 至 4 個 CPU [1]。
可以將MMP理解爲刀片服務器,每個刀扇裏的都是一臺獨立SMP架構服務器,並且每個刀扇之間均有高性能的網絡設備進行交互,保證smp服務器之間的數據傳輸性能。MMP架構比較依賴管理系統的處理能力來保障通信。
鎖模型
CLH鎖模型
是一種基於單向鏈表的、高性能、公平的自旋鎖。申請加鎖的線程通過前驅節點(pre-node)的變量進行自旋。當pre-node解鎖後,當前節點會結束自旋並進行加鎖。
CLH模型的邏輯:
- locked == true 表示節點處於加鎖狀態或者等待加鎖狀態。
- locked == false 表示節點處於解鎖狀態。
- 基於線程當前節點的前置節點的鎖值(locked)進行自旋,前置節點的 locked == true 自旋;當前置節點解鎖時,設置locked == false,後繼節點(就是當前節點)監聽到false,結束自旋。
- 每個節點在解鎖時更新自己的鎖值(locked),在這一時刻,該節點的後置節點會結束自旋,並進行加鎖。
由於自旋過程中,監控的是前置節點的變量,因此在SMP架構的共享內存模式,能更好的提供性能。
MCS鎖模型
與CLH鎖模型的最大區別是,監控的是自己的節點變量,當前置節點解鎖後,會主動修改自己的節點變量狀態。這種模型解決的是CLH模型在NUMA架構上的不足:當前置節點存在於其他CPU模塊時,自旋會導致頻繁的調用互聯模塊。是將自旋調整到了節點自身,互聯模塊的調用只存在於前置節點解鎖的時刻。
MCS模型的邏輯:
- locked == false 標識節點處於加鎖狀態(沒有自旋)
- locked == true 標識節點處於等待狀態(自旋)
- 基於當前節點的鎖值(locked)進行自旋,locked == true 自旋;當前置節點解鎖時,修改後繼節點(就是當前節點)的 locked == false ,進而結束當前節點的自旋。
- 每個節點在解鎖時更新後繼節點的鎖值(locked),在這一刻,該節點的後置節點會結束自旋,並進行加鎖。
說了這麼多,其實是想說明AQS中的鏈表,是爲了實現上面的模型,它是針對CLH鎖模型的一個變種。後面詳細描述AQS中針對head和tail的操作,實際上就是在操作鏈表的入隊和出隊。所以說,AQS中的大部分方法,就是在實現CLH鎖模型的邏輯。
AQS的功能
根據AQS的名稱:抽象隊列同步器,可以直白的知曉,
- 它是抽象類,自然有抽象方法。
- 它是一個隊列同步器,這裏的隊列我們可以指定是個鏈表
- 同步器,則是通過對鏈表及資源的操作,達到同步指標的目的(注意是同步的指標,它不等同於同步)
對於第三點,之所以這樣說,還有由於抽象類這個原因,從整體去看AQS,它的作用並不是去定義同步,而是去實現了一個CLH模型,直白點講就是實現瞭如何操作隊列及資源,至於何時達到同步,是由實現類去決定的。
由此可知:
- AQS中的實例方法:自身實現的用於操作隊列以及資源。
- AQS中的抽象方法:交給子類實現,來決定同步狀態。
尾聲
本文只是簡略的介紹了AQS的組成及每個成員的功能後續內容將會詳細展開每個成員,深挖它們的內在邏輯。
推薦閱讀