上一章《AQS源碼閱讀》講了AQS框架肥缔,這次講講它的應(yīng)用類(lèi)(注意不是子類(lèi)實(shí)現(xiàn)丛版,待會(huì)細(xì)講)巩掺。
ReentrantLock,顧名思義重入鎖页畦,但什么是重入胖替,這個(gè)鎖到底是怎樣的,我們來(lái)看看類(lèi)的注解說(shuō)明
ReentrantLock與隱式鎖synchronized功能相同豫缨,但ReentrantLock更具有擴(kuò)展性独令。
《鎖優(yōu)化》里提到Java在1.6對(duì)隱式鎖synchronized做了鎖的優(yōu)化,使其性能與顯式鎖性能相差無(wú)異好芭。所以在兩者的選擇上燃箭,更多的是考慮用法,以及功能上的擴(kuò)展栓撞。
ReentrantLock是線(xiàn)程獨(dú)占的遍膜,不能與其他線(xiàn)程共享。所謂的重入瓤湘,就是當(dāng)本線(xiàn)程想再次獲得鎖瓢颅,不需要重新申請(qǐng),它本身就已經(jīng)鎖了弛说,即重入該鎖挽懦。
為什么會(huì)允許鎖重入呢?因?yàn)樵摼€(xiàn)程已經(jīng)擁有鎖了木人,不會(huì)受其他線(xiàn)程干擾信柿,那么里面的共享變量就不會(huì)因?yàn)槎嗑€(xiàn)程執(zhí)行造成線(xiàn)程不安全。相當(dāng)于代碼已經(jīng)在串行執(zhí)行了醒第,沒(méi)必要再申請(qǐng)多余的鎖了渔嚷,而是重入當(dāng)前的鎖。
ReentrantLock會(huì)提供一個(gè)公平鎖的模式稠曼,如果選擇這個(gè)模式形病,會(huì)盡量使得獲取鎖是公平的,先來(lái)先得,但不一定嚴(yán)格按順序漠吻。
如果選擇了公平鎖量瓜,性能上會(huì)比不使用(默認(rèn))低一些。沒(méi)有一定保證順序途乃,同時(shí)也降性能绍傲,所以如果沒(méi)有特別的要求,盡量使用默認(rèn)的非公平鎖耍共。
現(xiàn)在基于以上的認(rèn)識(shí)烫饼,來(lái)看看ReentrantLock的基本實(shí)現(xiàn)吧。
ReentrantLock概覽
ReentrantLock是實(shí)現(xiàn)Lock接口的划提。所以主要的方法就是Lock接口定義的方法枫弟,包括lock()、tryLock()鹏往、unlock()淡诗、newCondition()等。
lock()與tryLock()的區(qū)別就是前者會(huì)一直等到直到獲取鎖伊履,后者則是嘗試在當(dāng)時(shí)獲取鎖韩容,不會(huì)重復(fù)去申請(qǐng)獲取。
這個(gè)newCondition()感覺(jué)比較突兀唐瀑,看起來(lái)完全不了解有什么用群凶,和Lock有什么關(guān)系,我們后面再詳細(xì)了解哄辣。
ReentrantLock里面有一個(gè)最核心的成員變量请梢,sync。sync的類(lèi)型就是內(nèi)部類(lèi)Sync力穗。它是AQS的子類(lèi)毅弧,也就是說(shuō)它就是實(shí)現(xiàn)ReentrantLock同步的工具。而FairSync和unFairSync則是Sync的子類(lèi)当窗,封裝了是否公平的功能够坐,用于賦值給sync成員變量。
Sync同步器
Sync是繼承上文所介紹的AQS崖面,是ReentrantLock里面的NonfairSync和FairSync的父類(lèi)元咙。
看注解可以知道,Sync用了AQS的state(狀態(tài)原子值)來(lái)標(biāo)識(shí)鎖被持有的數(shù)量巫员。
在AQS中庶香,tryRelease()是沒(méi)有定義的,所以在Sync中重寫(xiě)了简识。
先判斷下申請(qǐng)解鎖的線(xiàn)程是否獨(dú)占鎖的線(xiàn)程赶掖,否則拋出異常退出救军。
然計(jì)算新的state值,用當(dāng)前state減去releases值倘零。對(duì)于state值和releases值到底是多少,這里可以先留個(gè)懸念戳寸,但大家可以思考下上面注解的定義也可以大概猜出來(lái)呈驶。
最后判斷新state值是否為0,為0則沒(méi)有線(xiàn)程占用疫鹊,所以設(shè)當(dāng)前獨(dú)占線(xiàn)程為空袖瞻,并且更新state。這里更新state值并不需要用CAS原子操作拆吆,因?yàn)橹挥幸粋€(gè)線(xiàn)程會(huì)占用這個(gè)鎖聋迎,不是這個(gè)線(xiàn)程都異常退出了。
AQS中核心的tryAcquire()方法并沒(méi)有在這里實(shí)現(xiàn)枣耀,因?yàn)樽宇?lèi)NonfaiSync和FairSync的實(shí)現(xiàn)并不一樣霉晕。但這里同樣需要用到nonfairTryAcquire,所以抽象出來(lái)了捞奕。但為什么同樣需要牺堰,暫時(shí)不得而知,帶著問(wèn)題后面再看看颅围。
先判斷當(dāng)前鎖的state是否為0伟葫,為0則表示沒(méi)人獲取,然后通過(guò)CAS更新為acquires值(依然不知道值是多少)院促,同時(shí)更新當(dāng)前線(xiàn)程為鎖的獨(dú)占線(xiàn)程筏养。
如果state不為0,則表示有線(xiàn)程已經(jīng)占有了常拓。但可能占有的線(xiàn)程是當(dāng)前線(xiàn)程渐溶,那么當(dāng)前的state會(huì)加上acquires值。
這里很容易就看出來(lái)state就是代表重入的次數(shù)墩邀!所以上面的謎題就解開(kāi)了掌猛,releases,aquires都是代表每次申請(qǐng)的值,在ReentrantLock肯定都是1眉睹,他們的計(jì)算總值就是原子值state荔茬。
如果state不為0,也不是被當(dāng)前線(xiàn)程占用竹海,那么返回false獲取失敗慕蔚。
NonfairSync
沒(méi)啥特別的,直接調(diào)用Sync的方法斋配。也沒(méi)做修改孔飒。
FairSync
公平鎖的同步器灌闺。只有當(dāng)遞歸調(diào)用或者沒(méi)有其他等待者,再或者他自己本身排第一才能獲取鎖坏瞄。
這話(huà)比較繞口桂对,大概意思應(yīng)該是不停地輪詢(xún)申請(qǐng)鎖,直到自己排到隊(duì)列的第一才能獲取鸠匀。
乍看一看蕉斜,這個(gè)方法基本和父類(lèi)Sync的nonfairTryAcquire()一樣,唯一不同點(diǎn)就是在沒(méi)有線(xiàn)程占用的時(shí)候(state=0)缀棍,多了個(gè)
!hasQueuedPredecessors()
前置判斷宅此。這個(gè)方法用來(lái)判斷是否隊(duì)列為空,或者當(dāng)前線(xiàn)程是否在隊(duì)列的最前面爬范。
所以公平鎖模式下父腕,想要能獲取鎖,除非自己排在隊(duì)列的最前面青瀑。
綜上看璧亮,F(xiàn)airSync根本沒(méi)有調(diào)用到nonfairTryAcquire(),為何說(shuō)子類(lèi)都需要用到呢斥难?繼續(xù)留著懸念杜顺,后面解答。
@ReservedStackAccess
可以看到上面介紹的tryAcquire()和tryRelease()都有@ReservedStackAccess蘸炸。這個(gè)注解到底有什么用躬络?
查找了下資料,這個(gè)是JEP 270添加的新注解搭儒。它會(huì)保護(hù)被注解的方法穷当,通過(guò)添加一些額外的空間,防止在多線(xiàn)程運(yùn)行的時(shí)候出現(xiàn)棧溢出淹禾。具體看下圖
lock()馁菜、tryLock()成員函數(shù)
ReentrantLock里面的lock()方法是調(diào)用成員變量sync的acquire()。
無(wú)論是否公平鎖都是直接調(diào)用AQS的acquire()方法铃岔,不過(guò)就是各自有tryAcuqire()的重寫(xiě)汪疮,即上文所說(shuō)的內(nèi)容。
參數(shù)1毁习,是透?jìng)鹘otryAcquire()的智嚷,所以這里代表是入鎖一次的意思。
而tryLock()調(diào)用的是成員變量sync的nonfairTryAcquire()纺且。上文說(shuō)到Sync內(nèi)部類(lèi)抽象了這個(gè)方法出來(lái)盏道,說(shuō)到子類(lèi)都會(huì)用到,說(shuō)的正是tryLock()方法需要用到载碌。
所以顯而易見(jiàn)的猜嘱,無(wú)論是否公平鎖衅枫,調(diào)用tryLock()都是用的非公平鎖的方法。為什么呢朗伶?
因?yàn)閠ryLock()的try只是嘗試弦撩,無(wú)論是否公平,對(duì)于方法來(lái)說(shuō)沒(méi)有必要论皆,只是嘗試申請(qǐng)的時(shí)候能否獲取鎖而已孤钦。
至于其他成員函數(shù),大都是圍繞獲取線(xiàn)程和隊(duì)列的狀態(tài)纯丸,沒(méi)什么特別的,在這里不再贅述静袖,有興趣的可以看看源碼觉鼻。
總結(jié)
回顧下要點(diǎn)
- ReentrantLock是一個(gè)可重入的鎖(被當(dāng)前占用的線(xiàn)程重入)。
- 它有兩種模式公平與非公平队橙,通過(guò)NonfairSync和FairSync賦值sync成員變量實(shí)現(xiàn)坠陈。
- 兩種模式都是AQS的子類(lèi),通過(guò)重寫(xiě)tryAcquire()區(qū)別不同捐康。公平鎖多了是否在隊(duì)列的頭的判斷仇矾。
- tryLock()方法沒(méi)有區(qū)分模式,都是一樣的解总。
上文提到的newCondition()還沒(méi)有涉及到贮匕,等后續(xù)再起一章節(jié)說(shuō)下這個(gè)Condition。
如果覺(jué)得還不錯(cuò)花枫,請(qǐng)關(guān)注微信公眾號(hào):Zack說(shuō)碼