AQS,全稱AbstractQueuedSynchronizer
,是Concurrent包鎖的核心,沒有AQS就沒有Java的Concurrent包。它究竟是個什麼,咱們來看看源碼的第一段註解是怎麼說明
看完第一段,總結下java
基於以上概念,咱們看看源碼究竟是這麼實現這些功能的框架
private volatile int state;
該變量標記爲volatile
,說明該變量是對全部線程可見的。做用在於每一個線程改變該值,都會立刻讓其餘線程可見,在CAS(可見鎖概念與鎖優化)的時候是必不可少的。在AQS類中,不會直接操做這個值,而是交由它的子類去操做和定義他的做用。函數
AQS中有一個靜態內部類Node
,其實現是一個雙向鏈表。head
與tail
則是這個鏈表的頭尾指針。做用是存儲獲取鎖失敗的阻塞線程。一樣的,這個鏈表是會被多個線程操做的,因此它裏面的變量可能是被標記爲volatile
,而且操做也要經過CAS等原子方法去執行。
Node還有一個模式的屬性:獨佔模式和共享模式。獨佔模式下,鎖是線程獨佔的,而共享模式下,鎖是能夠被多個線程佔用的。工具
對於大多數須要操做的原子屬性,都對應會有一個大寫的值,它的類是VarHandler
。例如state、head、tail
都有對應的VarHandler,STATE、HEAD、TAIL
。VarHandler是1.9
的新特性,提供了相似於原子操做以及Unsafe操做的功能,裏面的原子操做大可能是native方法,比較難查看源碼。學習
條件隊列,是AQS中一個很是關鍵內部類。這個名字起很是奇異,讓人搞不懂,看它類註釋也看不懂說了什麼。看看AQS頭部註解
這個類是爲了讓子類支持獨佔模式的。深刻看其中的源碼實現,其實就是Node在功能性上的封裝,最終讓子類實現讓當前線程怎麼獨佔一個Object鎖。await()、dosign()
等方法就是讓線程阻塞、加入隊列、喚醒線程等。AQS框架下基本各類獨佔的加鎖,解鎖等操做到最後都是基於這個類實現的。該類是提供給子類去使用的,具體實現等下次說ReentranLock
再深刻了解。有人可能以爲爲何實現這個內部類,又不用,而是給子類去用,那爲何不放到子類去呢?其實答案,很簡單,抽象加模板模式。
p.s. 只有獨佔鎖才能配合該類使用。優化
AQS的公用的方法,主要是加鎖與解鎖方法。如下方法只提供了模板,部分實現仍是在子類當中,直接調用會拋出異常。ui
嘗試獲取鎖,失敗則進入隊列。
先執行tryAcquire()
(子類實現),成功則直接返回,若是是獲取鎖失敗,則執行addWaiter()
,經過CAS在雙向鏈表的尾部添加一個新獨佔節點。
而後把節點丟到acquireQueued()
中執行。該方法其實就是自旋嘗試獲取鎖或阻塞線程(子類實現決定)。一開始,獲取新節點的前驅節點,若是這個節點是head,則證實只有兩個節點,此時再次執行tryAcquire()
嘗試獲取鎖,若獲取成功,則不須要中斷,成功結束。
若是仍是獲取失敗,則執行shouldParkAfterFailedAcquire()
,根據前驅節點狀態(子類設值)判斷是否繼續自旋(當waitStatus爲初始值,重複上一步,直到前面的節點一直在減小到前驅節點爲head)或者阻塞線程(當waitStatus標記爲SIGNAL)
最後若是acquireQueued()
返回須要阻塞,則執行selfInterrupt()
設置線程爲中斷
能夠看回acquire()
函數的寫法,十分的藝術。利用條件判斷的短路規則,實如今if()
條件內嵌套判斷執行語音。通常人(筆者本人)若是要實現這個功能,會這麼寫spa
因此下次遇到相似嵌套if條件判斷的語句,能夠學習下acquire()
的這種短路寫法。贊👍線程
檢查線程是否被中斷並嘗試獲取鎖,失敗則進入隊列。線程中斷會退出隊列。
流程基本和acquire()
相同。不一樣點就是,acquireInterruptibly()
在自旋獲取過程當中若是線程是中斷的,那麼就會拋出異常退出流程,而且放棄鎖。doAcquireInterruptibly()
方法與acquireQueued()
方法很是類似,不一樣就是前者在中斷狀態下,不會再繼續獲取鎖。注意最後有cancelAcquire()
方法的執行。指針
嘗試獲取鎖,失敗則進入隊列。當超過指定時間或線程中斷會退出隊列。
在acquireInterruptibly()
基礎上,增長多一個時間判斷,超過指定時間,則退出,放棄獲取鎖。
釋放當前鎖,並喚醒下一個Node。
嘗試釋放鎖
若釋放成功,且waitStatus不爲0(證實是SIGNAL的),就會執行unparkSuccessor()
,先取消SIGNAL標誌,而後找到最近一個須要SIGNAL的節點,而且喚醒它。
以上方法皆爲獨佔模式,對應都有共享模式的方法。最大的不一樣其實就是Node的waitStatus值爲PROPAGATE。具體流程與獨佔大致相同,細節留到ReentrantReadWriteLock
再細瞭解。
回顧下要點
以上即便AQS的大體內容,可能有些部分難以理解,其實很正常,由於AQS提供的是流程模板與工具,沒有實質落地的場景,是比較難理解的。等後面介紹ReentranLock
與ReentrantReadWriteLock
的時候,就能夠更好更全面的瞭解總體AQS框架了。
若是以爲還不錯,請關注公衆號:Zack說碼