AQS脉让,全稱AbstractQueuedSynchronizer
副渴,是Concurrent包鎖的核心信卡,沒有AQS就沒有Java的Concurrent包。它到底是個什么,我們來看看源碼的第一段注解是怎么說明
看完第一段鹦赎,總結下
- AQS是一個同步的基礎框架谍椅,基于一個先進先出的隊列。
- 鎖機制基于一個狀態(tài)值古话,它是原子值雏吭。
- AQS的子類負責定義與操作這個狀態(tài)值,但必須通過AQS提供的原子操作
- AQS剩余的方法就是圍繞隊列陪踩,與線程阻塞喚醒等功能
基于以上概念杖们,我們看看源碼到底是這么實現(xiàn)這些功能的
AQS的成員變量
state
private volatile int state;
該變量標記為volatile
,說明該變量是對所有線程可見的肩狂。作用在于每個線程改變該值摘完,都會馬上讓其他線程可見,在CAS(可見鎖概念與鎖優(yōu)化)的時候是必不可少的傻谁。在AQS類中孝治,不會直接操作這個值,而是交由它的子類去操作和定義他的作用栅螟。
Node荆秦、head、tail
AQS中有一個靜態(tài)內部類Node
力图,其實現(xiàn)是一個雙向鏈表步绸。head
與tail
則是這個鏈表的頭尾指針。作用是存儲獲取鎖失敗的阻塞線程吃媒。同樣的瓤介,這個鏈表是會被多個線程操作的,所以它里面的變量多是被標記為volatile
赘那,并且操作也要通過CAS等原子方法去執(zhí)行刑桑。
Node還有一個模式的屬性:獨占模式和共享模式。獨占模式下募舟,鎖是線程獨占的祠斧,而共享模式下,鎖是可以被多個線程占用的拱礁。
VarHandler
對于大多數(shù)需要操作的原子屬性琢锋,都對應會有一個大寫的值,它的類是VarHandler
呢灶。例如state吴超、head、tail
都有對應的VarHandler鸯乃,STATE鲸阻、HEAD、TAIL
。VarHandler是1.9
的新特性鸟悴,提供了類似于原子操作以及Unsafe操作的功能陈辱,里面的原子操作大多是native方法,比較難查看源碼遣臼。
ConditionObject
條件隊列性置,是AQS中一個非常關鍵內部類。這個名字起非常奇異揍堰,讓人搞不懂,看它類注釋也看不懂說了什么嗅义∑链酰看看AQS頭部注解
這個類是為了讓子類支持獨占模式的。深入看其中的源碼實現(xiàn)之碗,其實就是Node在功能性上的封裝蝙眶,最終讓子類實現(xiàn)讓當前線程怎么獨占一個Object鎖。
await()褪那、dosign()
等方法就是讓線程阻塞幽纷、加入隊列、喚醒線程等博敬。AQS框架下基本各種獨占的加鎖友浸,解鎖等操作到最后都是基于這個類實現(xiàn)的。該類是提供給子類去使用的偏窝,具體實現(xiàn)等下次說ReentranLock
再深入了解收恢。有人可能覺得為什么實現(xiàn)這個內部類,又不用祭往,而是給子類去用伦意,那為什么不放到子類去呢?其實答案硼补,很簡單驮肉,抽象加模板模式。p.s. 只有獨占鎖才能配合該類使用已骇。
AQS的成員函數(shù)
AQS的公用的方法离钝,主要是加鎖與解鎖方法。以下方法只提供了模板疾捍,部分實現(xiàn)還是在子類當中奈辰,直接調用會拋出異常。
acquire()
嘗試獲取鎖乱豆,失敗則進入隊列奖恰。
先執(zhí)行
tryAcquire()
(子類實現(xiàn)),成功則直接返回,如果是獲取鎖失敗瑟啃,則執(zhí)行addWaiter()
论泛,通過CAS在雙向鏈表的尾部添加一個新獨占節(jié)點。然后把節(jié)點丟到
acquireQueued()
中執(zhí)行蛹屿。該方法其實就是自旋嘗試獲取鎖或阻塞線程(子類實現(xiàn)決定)屁奏。一開始,獲取新節(jié)點的前驅節(jié)點错负,如果這個節(jié)點是head坟瓢,則證明只有兩個節(jié)點,此時再次執(zhí)行tryAcquire()
嘗試獲取鎖犹撒,若獲取成功折联,則不需要中斷,成功結束识颊。如果還是獲取失敗诚镰,則執(zhí)行
shouldParkAfterFailedAcquire()
,根據(jù)前驅節(jié)點狀態(tài)(子類設值)判斷是否繼續(xù)自旋(當waitStatus為初始值祥款,重復上一步清笨,直到前面的節(jié)點一直在減少到前驅節(jié)點為head)或者阻塞線程(當waitStatus標記為SIGNAL)最后如果
acquireQueued()
返回需要阻塞,則執(zhí)行selfInterrupt()
設置線程為中斷可以看回
acquire()
函數(shù)的寫法刃跛,十分的藝術抠艾。利用條件判斷的短路規(guī)則,實現(xiàn)在if()
條件內嵌套判斷執(zhí)行語音奠伪。一般人(筆者本人)如果要實現(xiàn)這個功能跌帐,會這么寫
所以下次遇到類似嵌套if條件判斷的語句,可以學習下acquire()
的這種短路寫法绊率。贊??
acquireInterruptibly()
檢查線程是否被中斷并嘗試獲取鎖谨敛,失敗則進入隊列。線程中斷會退出隊列滤否。
流程基本和acquire()
相同脸狸。不同點就是,acquireInterruptibly()
在自旋獲取過程中如果線程是中斷的藐俺,那么就會拋出異常退出流程炊甲,并且放棄鎖。
doAcquireInterruptibly()
方法與acquireQueued()
方法非常相似欲芹,不同就是前者在中斷狀態(tài)下卿啡,不會再繼續(xù)獲取鎖。注意最后有cancelAcquire()
方法的執(zhí)行菱父。tryAcquireNanos()
嘗試獲取鎖颈娜,失敗則進入隊列剑逃。當超過指定時間或線程中斷會退出隊列。
在acquireInterruptibly()
基礎上官辽,增加多一個時間判斷蛹磺,超過指定時間,則退出同仆,放棄獲取鎖萤捆。
release()
釋放當前鎖,并喚醒下一個Node俗批。
嘗試釋放鎖
若釋放成功俗或,且waitStatus不為0(證明是SIGNAL的),就會執(zhí)行
unparkSuccessor()
扶镀,先取消SIGNAL標志蕴侣,然后找到最近一個需要SIGNAL的節(jié)點,并且喚醒它臭觉。**shared()
以上方法皆為獨占模式,對應都有共享模式的方法辱志。最大的不同其實就是Node的waitStatus值為PROPAGATE蝠筑。具體流程與獨占大體相同,細節(jié)留到ReentrantReadWriteLock
再細了解揩懒。
總結
回顧下要點
- AQS是一個同步的基礎框架什乙,是ReentranLock、ReentranReadWriteLock的父類
- AQS原理是維護一個state原子值已球,通過一個雙向鏈表的隊列實現(xiàn)同步臣镣。
- 對于state、與隊列的操作都是原子操作智亮,通過VarHandle實現(xiàn)
- 主要對外方法是加鎖與解鎖忆某,區(qū)別是否中斷、超時阔蛉、共享或獨占模式
以上即使AQS的大致內容弃舒,可能有些部分難以理解,其實很正常状原,因為AQS提供的是流程模板與工具聋呢,沒有實質落地的場景,是比較難理解的颠区。等后面介紹ReentranLock
與ReentrantReadWriteLock
的時候削锰,就可以更好更全面的了解整體AQS框架了。
如果覺得還不錯毕莱,請關注微信公眾號:Zack說碼