Java多線程——AQS框架源碼閱讀
AbstractQueuedSynchronizer
,是Concurrent包鎖的核心,沒有AQS就沒有Java的Concurrent包。它到底是個什麽,我們來看看源碼的第一段註解是怎麽說明看完第一段,總結下
- AQS是一個同步的基礎框架,基於一個先進先出的隊列。
- 鎖機制基於一個狀態值,它是原子值。
- AQS的子類負責定義與操作這個狀態值,但必須通過AQS提供的原子操作
- AQS剩余的方法就是圍繞隊列,與線程阻塞喚醒等功能
基於以上概念,我們看看源碼到底是這麽實現這些功能的
AQS的成員變量
state
private volatile int state;
該變量標記為volatile
Node、head、tail
AQS中有一個靜態內部類Node
,其實現是一個雙向鏈表。head
與tail
則是這個鏈表的頭尾指針。作用是存儲獲取鎖失敗的阻塞線程。同樣的,這個鏈表是會被多個線程操作的,所以它裏面的變量多是被標記為volatile
,並且操作也要通過CAS等原子方法去執行。
Node還有一個模式的屬性:獨占模式和共享模式。獨占模式下,鎖是線程獨占的,而共享模式下,鎖是可以被多個線程占用的。
VarHandler
對於大多數需要操作的原子屬性,都對應會有一個大寫的值,它的類是VarHandler
。例如state、head、tail
都有對應的VarHandler,STATE、HEAD、TAIL
。VarHandler是1.9
的新特性,提供了類似於原子操作以及Unsafe操作的功能,裏面的原子操作大多是native方法,比較難查看源碼。
ConditionObject
條件隊列,是AQS中一個非常關鍵內部類。這個名字起非常奇異,讓人搞不懂,看它類註釋也看不懂說了什麽。看看AQS頭部註解
這個類是為了讓子類支持獨占模式的。深入看其中的源碼實現,其實就是Node在功能性上的封裝,最終讓子類實現讓當前線程怎麽獨占一個Object鎖。await()、dosign()
ReentranLock
再深入了解。有人可能覺得為什麽實現這個內部類,又不用,而是給子類去用,那為什麽不放到子類去呢?其實答案,很簡單,抽象加模板模式。p.s. 只有獨占鎖才能配合該類使用。
AQS的成員函數
AQS的公用的方法,主要是加鎖與解鎖方法。以下方法只提供了模板,部分實現還是在子類當中,直接調用會拋出異常。
acquire()
嘗試獲取鎖,失敗則進入隊列。
先執行tryAcquire()
(子類實現),成功則直接返回,如果是獲取鎖失敗,則執行addWaiter()
,通過CAS在雙向鏈表的尾部添加一個新獨占節點。
然後把節點丟到acquireQueued()
中執行。該方法其實就是自旋嘗試獲取鎖或阻塞線程(子類實現決定)。一開始,獲取新節點的前驅節點,如果這個節點是head,則證明只有兩個節點,此時再次執行tryAcquire()
嘗試獲取鎖,若獲取成功,則不需要中斷,成功結束。
如果還是獲取失敗,則執行shouldParkAfterFailedAcquire()
,根據前驅節點狀態(子類設值)判斷是否繼續自旋(當waitStatus為初始值,重復上一步,直到前面的節點一直在減少到前驅節點為head)或者阻塞線程(當waitStatus標記為SIGNAL)
最後如果acquireQueued()
返回需要阻塞,則執行selfInterrupt()
設置線程為中斷
可以看回acquire()
函數的寫法,十分的藝術。利用條件判斷的短路規則,實現在if()
條件內嵌套判斷執行語音。一般人(筆者本人)如果要實現這個功能,會這麽寫
所以下次遇到類似嵌套if條件判斷的語句,可以學習下acquire()
的這種短路寫法。贊
Java多線程——AQS框架源碼閱讀