Java多線程——AQS框架源碼閱讀

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)是一個雙向鏈表步绸。headtail則是這個鏈表的頭尾指針。作用是存儲獲取鎖失敗的阻塞線程吃媒。同樣的瓤介,這個鏈表是會被多個線程操作的,所以它里面的變量多是被標記為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再細了解揩懒。

總結

回顧下要點

  1. AQS是一個同步的基礎框架什乙,是ReentranLock、ReentranReadWriteLock的父類
  2. AQS原理是維護一個state原子值已球,通過一個雙向鏈表的隊列實現(xiàn)同步臣镣。
  3. 對于state、與隊列的操作都是原子操作智亮,通過VarHandle實現(xiàn)
  4. 主要對外方法是加鎖與解鎖忆某,區(qū)別是否中斷、超時阔蛉、共享或獨占模式

以上即使AQS的大致內容弃舒,可能有些部分難以理解,其實很正常状原,因為AQS提供的是流程模板與工具聋呢,沒有實質落地的場景,是比較難理解的颠区。等后面介紹ReentranLockReentrantReadWriteLock的時候削锰,就可以更好更全面的了解整體AQS框架了。

如果覺得還不錯毕莱,請關注微信公眾號:Zack說碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末器贩,一起剝皮案震驚了整個濱河市颅夺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌磨澡,老刑警劉巖碗啄,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異稳摄,居然都是意外死亡稚字,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門厦酬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胆描,“玉大人,你說我怎么就攤上這事仗阅〔玻” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵减噪,是天一觀的道長短绸。 經常有香客問我,道長筹裕,這世上最難降的妖魔是什么醋闭? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮朝卒,結果婚禮上证逻,老公的妹妹穿的比我還像新娘。我一直安慰自己抗斤,他們只是感情好囚企,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瑞眼,像睡著了一般龙宏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上负拟,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天烦衣,我揣著相機與錄音,去河邊找鬼掩浙。 笑死花吟,一個胖子當著我的面吹牛,可吹牛的內容都是我干的厨姚。 我是一名探鬼主播衅澈,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谬墙!你這毒婦竟也來了今布?” 一聲冷哼從身側響起经备,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎部默,沒想到半個月后侵蒙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡傅蹂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年纷闺,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片份蝴。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡犁功,死狀恐怖,靈堂內的尸體忽然破棺而出婚夫,到底是詐尸還是另有隱情浸卦,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布案糙,位于F島的核電站限嫌,受9級特大地震影響,放射性物質發(fā)生泄漏时捌。R本人自食惡果不足惜萤皂,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望匣椰。 院中可真熱鬧,春花似錦端礼、人聲如沸禽笑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽佳镜。三九已至,卻和暖如春凡桥,著一層夾襖步出監(jiān)牢的瞬間蟀伸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工缅刽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留啊掏,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓衰猛,卻偏偏與公主長得像迟蜜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子啡省,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容