照例浓恶,首先看看JUC中Locks的結(jié)構(gòu)
看著很短固以,但是有關(guān)鎖的內(nèi)容卻很長(zhǎng)掂僵,需要時(shí)間消化
大致我們可以從以下幾個(gè)方面來(lái)講解
鎖是JUC中至關(guān)重要的內(nèi)容稿辙,日常開發(fā)中也用的很多昆码,對(duì)于并發(fā)而言更是至關(guān)重要,因此在面試中被問起的概率非常高邻储,所以對(duì)于這一塊的內(nèi)容要著重掌握
一赋咽、locks的概念
java提供各類鎖的一個(gè)框架。
二吨娜、locks分類
(一)按鎖的性質(zhì)劃分
1脓匿、按多個(gè)線程是否按順序獲取鎖的角度劃分
a、公平鎖
當(dāng)鎖被某個(gè)線程持有時(shí)宦赠,新發(fā)出請(qǐng)求的線程會(huì)被放入隊(duì)列中陪毡,在公平鎖中,每一次嘗試獲取鎖都會(huì)檢查CLH隊(duì)列中是否仍有前驅(qū)的元素勾扭,如果仍然有那么繼續(xù)等待毡琉。
獲取方法:公平鎖在獲取鎖之前會(huì)先判斷等待隊(duì)列是否為空或者自己位于隊(duì)列首部,如果為true則可以繼續(xù)獲取鎖妙色,否則就入隊(duì)等待桅滋。
缺點(diǎn):公平鎖為了保證線程排隊(duì),需要增加阻塞和喚醒的時(shí)間開銷
源碼分析:
深入看看hasQueuePredecessors()身辨,作用是判斷此線程是否應(yīng)該放入等待隊(duì)列丐谋。首先說明一下這個(gè)等待隊(duì)列是個(gè)頭結(jié)點(diǎn)不存在元素的隊(duì)列。h != t && ((s = h.next) == null || s.thread != Thread.currentThread());表示此隊(duì)列不為空并且這個(gè)線程不在頭結(jié)點(diǎn)處煌珊。
b笋鄙、非公平鎖
非公平鎖和公平鎖在獲取鎖的方法上,流程是一樣的怪瓶;它們的區(qū)別主要表現(xiàn)在“嘗試獲取鎖的機(jī)制不同”
獲取方法:無(wú)視等待隊(duì)列直接嘗試獲取鎖萧落,如果鎖是空閑的践美,即可獲取狀態(tài),則獲取鎖
優(yōu)點(diǎn):可以減少喚起線程的開銷
缺點(diǎn):處于等待隊(duì)列中的線程可能會(huì)出現(xiàn)一直獲取不到鎖的現(xiàn)象(餓死)
饑餓原因:
1.高優(yōu)先級(jí)線程占用所有CPU找岖,低優(yōu)先級(jí)進(jìn)程一直無(wú)法獲取
2.一個(gè)線程每次競(jìng)爭(zhēng)鎖都失敗陨倡,而新的線程還在一直不斷競(jìng)爭(zhēng),從而導(dǎo)致這個(gè)線程幾乎是一直處于等待中
3.某個(gè)線程在某個(gè)對(duì)象的條件隊(duì)列上等待许布,而其他線程不斷搶入
final void lock() {
????if (compareAndSetState(0, 1))
????????setExclusiveOwnerThread(Thread.currentThread());
????else
????????acquire(1);
}
可以看到獲取鎖的時(shí)候是直接去獲取的兴革,compareAndSetState(0,1)修改鎖的狀態(tài),修改成功則表示鎖已經(jīng)被釋放了蜜唾,然后把占有鎖的線程改為本線程杂曲。
PS:ReentrantLock、ReadWriteLock袁余、Synchronized默認(rèn)都是非公平模式
c.使用場(chǎng)景
公平鎖:線程處理時(shí)間過長(zhǎng)擎勘,一部分客戶端獲取不到服務(wù)(違反公平原則,一直被新的線程占用CPU)
非公平鎖:當(dāng)線程吞吐量大的時(shí)候
2颖榜、按多個(gè)線程是否可以持有同一把鎖的角度劃分
a.獨(dú)享鎖(互斥鎖/寫鎖):
是指該鎖一次只能被一個(gè)線程鎖持有
ReentrantLock和Synchronized都是獨(dú)占鎖棚饵。
ReentrantReadWriteLock為讀寫鎖,對(duì)于讀操作是共享鎖掩完,對(duì)于寫操作是獨(dú)占鎖噪漾。這樣可以做到讀讀操作不互斥,但是讀寫且蓬、寫讀和寫寫操作都是互斥的欣硼。
PS:CopyOnWriteArrayList,對(duì)它的操作可以做到寫寫互斥、其他三個(gè)操作不互斥恶阴。
b.共享鎖(讀鎖)
是指該鎖可被多個(gè)線程所持有
3分别、按一個(gè)線程能否重復(fù)獲取自己的所得角度劃分
a.重入鎖(遞歸鎖)
同一個(gè)線程而言,它可以重復(fù)的獲取鎖存淫,避免同一個(gè)線程重復(fù)獲取鎖發(fā)生死鎖。
外層方法獲得鎖之后沼填,進(jìn)入內(nèi)層方法調(diào)用的方法自動(dòng)獲得桅咆,并不會(huì)阻塞。(同一個(gè)線程坞笙,可以多次獲得同一把可重入鎖)
ReentrantLock就是重入鎖岩饼,如下:
可以看到,在print()方法的同步塊中薛夜,調(diào)用dosomething()方法時(shí)又獲得了這把鎖籍茧,這個(gè)時(shí)候這把鎖還沒有釋放,也就是上一節(jié)中的代碼中的state并不為0梯澜,但由于是可充入鎖寞冯,所以并不會(huì)阻塞,而是將acquires變量+1。注意可重入鎖的可重入性只針對(duì)本線程吮龄。
b.不重入鎖
一個(gè)線程多次請(qǐng)求同一把鎖俭茧,會(huì)出現(xiàn)死鎖
優(yōu)點(diǎn):效率更高
缺點(diǎn):易發(fā)生死鎖
(二)按鎖的設(shè)計(jì)方案來(lái)分類
1、按多個(gè)線程競(jìng)爭(zhēng)同一把鎖是否進(jìn)行阻塞劃分
a.自旋鎖
當(dāng)有另外一個(gè)線程來(lái)競(jìng)爭(zhēng)鎖時(shí)漓帚,這個(gè)線程會(huì)在原地等待母债,而不是把該線程給阻塞,直到那個(gè)獲得鎖的線程釋放之后尝抖,這個(gè)線程馬上獲得鎖
自旋鎖的應(yīng)用就是CAS操作 +?循環(huán)
b.自適應(yīng)自旋鎖
自旋的時(shí)間(次數(shù))不再固定毡们,而是由前一次在同一個(gè)鎖上的自選時(shí)間及鎖的擁有者狀態(tài)來(lái)決定:
1.如果同一個(gè)鎖對(duì)象上,自旋等待剛剛成功獲得過鎖昧辽,并且持有鎖的線程正在運(yùn)行中衙熔,那么虛擬機(jī)就會(huì)認(rèn)為這次自旋也很有可能再次成功,進(jìn)而它將允許自旋等待持續(xù)相對(duì)更長(zhǎng)的時(shí)間奴迅;
2.如果對(duì)于某個(gè)鎖青责,自旋很少成功獲得過,那在以后嘗試獲取這個(gè)鎖時(shí)將可能省略掉自旋過程取具,直接阻塞進(jìn)程脖隶,避免浪費(fèi)處理器資源。
c.優(yōu)缺點(diǎn)分析
自旋鎖是一種非阻塞鎖暇检,如果某線程需要獲取自旋鎖产阱,但該鎖已經(jīng)被其他線程占用時(shí),該線程不會(huì)被掛起块仆,而是在不斷的消耗CPU的時(shí)間构蹬,不停的試圖獲取自旋鎖,自旋鎖不會(huì)使線程狀態(tài)發(fā)生切換執(zhí)行速度快悔据,但是會(huì)不斷消耗CPU庄敛。
2、按多個(gè)線程并發(fā)時(shí)鎖的力度以及鎖競(jìng)爭(zhēng)劃分
a.分段鎖
在鎖發(fā)生競(jìng)爭(zhēng)時(shí)使用獨(dú)享鎖保護(hù)受限資源科汗,一次只能有一個(gè)線程能訪問獨(dú)享鎖藻烤,但是這樣的話效率很低,因此可以采用一種機(jī)制來(lái)協(xié)調(diào)獨(dú)享鎖進(jìn)而提高并發(fā)性头滔,這個(gè)機(jī)制就是分段鎖怖亭。
案例:ConcurrentHashMap(JDK1.7)支持多達(dá)16個(gè)并發(fā)的寫入操作
如圖所示,ConcurrentHashMap默認(rèn)分成了16個(gè)segment坤检,每個(gè)segment都對(duì)應(yīng)一個(gè)Hash表兴猩,且都由獨(dú)立的鎖(ReetrantLock)。所以這樣就每個(gè)線程訪問一個(gè)Segment早歇,就可以并行訪問了倾芝,從而提高了效率讨勤,這就是鎖分段。
缺點(diǎn):與單個(gè)鎖相比蛀醉,獲取多個(gè)鎖來(lái)實(shí)現(xiàn)獨(dú)占訪問將更加困難并且開銷更大
3悬襟、按多個(gè)程序?qū)懖僮鞲蓴_本身線程的程度劃分
a.樂觀鎖:認(rèn)為每次讀取數(shù)據(jù)時(shí),不會(huì)進(jìn)行其他寫操作拯刁,如CAS
b.悲觀鎖:認(rèn)為每次讀取數(shù)據(jù)時(shí)脊岳,都會(huì)有寫操作,如synchronized
c.優(yōu)缺點(diǎn)分析
悲觀鎖是指當(dāng)一個(gè)線程獲取鎖時(shí)其他線程只能進(jìn)行阻塞垛玻,并且進(jìn)程掛起和恢復(fù)執(zhí)行過程中也存在著很大的開銷
CAS是項(xiàng)樂觀鎖技術(shù)割捅,當(dāng)多個(gè)線程嘗試使用CAS同時(shí)更新同一個(gè)變量時(shí),只有其中一個(gè)線程能更新變量的值帚桩,而其他線程都失敗亿驾,失敗的線程并不會(huì)被掛起,而是被告知這次競(jìng)爭(zhēng)失敗账嚎,并可以再次嘗試莫瞬。
PS:使用樂觀鎖如果存在大量寫操作,需要不斷重試?yán)速M(fèi)CPU資源
4郭蕉、按多個(gè)線程競(jìng)爭(zhēng)程度劃分
a.偏向鎖:只有一個(gè)申請(qǐng)鎖的線程使用鎖
b.輕量鎖:多個(gè)線程交替使用鎖疼邀,允許短時(shí)間的鎖競(jìng)爭(zhēng)
c.重量鎖:有實(shí)際競(jìng)爭(zhēng),且鎖競(jìng)爭(zhēng)的時(shí)間長(zhǎng)
優(yōu)缺點(diǎn)分析::
1.如果一個(gè)同步方法召锈,沒有多線程競(jìng)爭(zhēng)旁振,并且總是由同一個(gè)線程多次獲取鎖,如果每次還有阻塞線程涨岁,那么對(duì)CPU時(shí)一種資源的浪費(fèi)拐袜,為了解決這類問題,誕生了偏向鎖(偏向鎖只需要在置換ThreadID的時(shí)候依賴一次CAS原子指令梢薪,因?yàn)橹挥幸粋€(gè)線程在競(jìng)爭(zhēng)蹬铺,我們只要去判斷該偏向鎖中的ThreadID是否為當(dāng)前線程即可);
2.輕量級(jí)鎖是通過CAS來(lái)避免進(jìn)入開銷較大的互斥操作,CAS是無(wú)鎖的可以避免線程狀態(tài)切換帶來(lái)的開銷秉撇,但是不適合鎖競(jìng)爭(zhēng)時(shí)間長(zhǎng)(線程計(jì)算慢)甜攀,非常浪費(fèi)CPU資源
3.重量級(jí)鎖更適合時(shí)間長(zhǎng)的情況下使用,可以避免CPU浪費(fèi).
PS:使用Synchronized有進(jìn)行用戶態(tài)到內(nèi)核態(tài)的切換畜疾,這個(gè)過程不是一觸而就的,而是經(jīng)歷了偏向鎖印衔、輕量鎖啡捶、重量鎖三個(gè)過程。
三奸焙、為什么使用Lock
? ? ? ?為了解決synchronized阻塞帶來(lái)的性能問題瞎暑,JDK1.5提供了線程同步工具Lock接口方便實(shí)現(xiàn)各類鎖彤敛,從Lock提供的各類鎖的角度來(lái)說,對(duì)比synchronized這種悲觀鎖要顯得更加豐富和靈活了赌。
(一)Synchronized
synchronized是java中的一個(gè)關(guān)鍵字墨榄,也就是說是Java語(yǔ)言內(nèi)置的特性。
Synchronized是由monitor管程的ObjectMonitor來(lái)實(shí)現(xiàn)請(qǐng)求和等待的:
1.其中ObjectMonitor有兩個(gè)隊(duì)列勿她,用來(lái)保存ObjectWaiter對(duì)象列表(每個(gè)等待鎖的線程都會(huì)被封裝成ObjectWaiter對(duì)象)
????????????_WaitSet
????????????_EntryList
2._Owner指向持有持有Monitor對(duì)象的線程袄秩,當(dāng)多個(gè)線程同時(shí)訪問同一段同步代碼時(shí),首先會(huì)進(jìn)入_EntryList集合
當(dāng)線程獲取到對(duì)象的Monitor后進(jìn)入_Owner區(qū)域逢并,并把Monitor中的owner變量設(shè)置為當(dāng)前線程Monitor中的計(jì)數(shù)器count+1
若當(dāng)前線程執(zhí)行完畢也將釋放monitor(鎖)并復(fù)位變量的值之剧,以便其它線程進(jìn)入獲取monitor(鎖)
3.Synchronized在進(jìn)行重量級(jí)鎖之前會(huì)先歷經(jīng)偏向鎖、輕量級(jí)鎖和自旋鎖
不直接切換成重量級(jí)的鎖的原因是重量級(jí)鎖需要進(jìn)行操作系統(tǒng)Mutex Lock來(lái)實(shí)現(xiàn)砍聊,線程之間的切換需要從用戶狀態(tài)到核心態(tài)
4.Synchronized缺陷(重量級(jí)鎖/悲觀鎖的缺陷)
a.當(dāng)一個(gè)代碼塊被Synchronized修飾了背稼,如果一個(gè)線程獲取了對(duì)應(yīng)的鎖,并執(zhí)行該代碼塊時(shí)玻蝌,其他線程便只能一直等待蟹肘,等待獲取鎖的線程釋放鎖,而這里獲取鎖的線程釋放鎖只會(huì)有兩種情況
????獲取鎖的線程執(zhí)行完了該代碼塊俯树,然后線程釋放對(duì)鎖的占有帘腹;
????線程執(zhí)行發(fā)生異常,此時(shí)JVM會(huì)讓線程自動(dòng)釋放鎖聘萨。
b.若這個(gè)鎖獲取的線程由于要等待IO或其他原因(如調(diào)用sleep方法)被阻塞了竹椒,但是又沒有釋放鎖,其他線程便只能等待
c.程序執(zhí)行效率低下
(二)Lock和Synchronized的區(qū)別
1米辐、Synchronized是屬于虛擬機(jī)層面胸完,Lock是屬于api層面,前者是關(guān)鍵字翘贮,后者是java實(shí)現(xiàn)的類赊窥。也就是說前者是在JVM內(nèi)部實(shí)現(xiàn)得,Lock是在語(yǔ)言層面實(shí)現(xiàn)的狸页。
2锨能、JVM內(nèi)部使用的是monitorenter和monitorexit指令實(shí)現(xiàn);Lock基本的是用state變量來(lái)判斷是否被鎖芍耘。
3址遇、Synchronized不用手動(dòng)釋放鎖,代碼執(zhí)行完了自動(dòng)釋放斋竞,而Lock要手動(dòng)釋放倔约。lock()和unlock()方法必須配對(duì)使用。
4坝初、Synchronized是非公平鎖浸剩,Lock默認(rèn)是非公平鎖钾军,但是可以設(shè)置成公平鎖。
5绢要、Synchronized不能中斷吏恭,必須拋出異常或者代碼執(zhí)行完重罪。而Lock可以中斷氧枣,這也是Lock的特點(diǎn):
(1)可以設(shè)置超時(shí)方法tryLock(long timeout, TimeUnit unit);
(2)上鎖的時(shí)候可以上可中斷鎖:lockInterruptibly()年缎,線程阻塞在鎖著的時(shí)候可以在其它線程中調(diào)用interrupter()方法中斷蓬衡,拋出InterruptedException異常以供處理碎捺,如下例:
Synchronized喚醒線程時(shí)只能隨機(jī)喚醒,Lock可以搭配Condition實(shí)現(xiàn)精準(zhǔn)喚醒某個(gè)線程惨篱。通過聲明多個(gè)Condition來(lái)實(shí)現(xiàn)盏筐,喚醒時(shí)喚醒阻塞在某個(gè)Condition上的線程。
四砸讳、Lock API
1琢融、lock()?獲得鎖,否則一直等待
2簿寂、unlock()?釋放鎖
3漾抬、tryLock()?判斷鎖狀態(tài),鎖被占用就返回false,否則返回true
4常遂、tryLock(Long time, TimeUnit unie)?比起tryLock()添加一個(gè)時(shí)間期限判斷
5纳令、lockInterruptibly()?此種獲取鎖的方式,鎖被占用的情況下拋出異常克胳,直接終端平绩,可以去做其他處理
6、Condition newCondition()?通知組件:具有阻塞與喚醒功能
五漠另、Condition
Object類中的wait()捏雌、notify()、notifyAll()方法實(shí)現(xiàn)了線程之間的通信笆搓,而Condition類中的await()性湿、signal()、signalAll()方法也實(shí)現(xiàn)了相似的功能满败。通過Condition能夠精細(xì)的控制多線程的休眠與喚醒肤频, 對(duì)于一個(gè)鎖,我們可以為多個(gè)線程間建立不同的Condition算墨。? ? ??
ConditionObject是同步器AbstractQueuedSynchronizer的內(nèi)部類宵荒,每個(gè)Condition對(duì)象都包含著一個(gè)隊(duì)列,該隊(duì)列是Condition對(duì)象實(shí)現(xiàn)等待/通知功能的關(guān)鍵
同步隊(duì)列:AQS的同步排隊(duì)用了一個(gè)隱式的雙向隊(duì)列,同步隊(duì)列的每個(gè)節(jié)點(diǎn)是一個(gè)AbstractQueuedSynchronizer.Node實(shí)例
等待隊(duì)列:Conditon的等待隊(duì)列用了一個(gè)隱式的單向隊(duì)列骇扇,等待隊(duì)列的每個(gè)節(jié)點(diǎn)是一個(gè)AbstractQueuedSynchronizer.Node實(shí)例
總結(jié):
1:整個(gè)過程是節(jié)點(diǎn)在同步隊(duì)列和等待隊(duì)列中來(lái)回移動(dòng)實(shí)現(xiàn)的
2:每當(dāng)一個(gè)線程調(diào)用Condition.await()方法,那么該線程會(huì)釋放鎖面粮,構(gòu)造成一個(gè)Node節(jié)點(diǎn)加入到等待隊(duì)列的隊(duì)尾少孝,自旋判斷如果當(dāng)前節(jié)點(diǎn)沒有在同步隊(duì)列上則將當(dāng)前線程阻塞
3:調(diào)用Condition的signal()方法,會(huì)將節(jié)點(diǎn)移到同步隊(duì)列中熬苍,但不一定在隊(duì)首
4:如果退出自旋說明當(dāng)前節(jié)點(diǎn)已經(jīng)在同步隊(duì)列上稍走。通過acquireQueued將阻塞直到當(dāng)前節(jié)點(diǎn)成為隊(duì)首,即當(dāng)前線程獲得了鎖柴底。然后await()方法就可以退出了婿脸,讓線程繼續(xù)執(zhí)行await()后的代碼
六、AbstractQueuedSynchronizer(AQS)
AQS是locks的核心柄驻,就好像前端控制器對(duì)于springMVC來(lái)說一樣狐树。
AQS提供一個(gè)框架,一個(gè)FIFO的等待隊(duì)列和一個(gè)代表狀態(tài)的int值鸿脓,子類需要定義這個(gè)狀態(tài)的protected方法抑钟,定義什么狀態(tài)獲取到狀態(tài)以及釋放鎖狀態(tài),該類方法提供所有入隊(duì)和阻塞機(jī)制野哭,AQS框架提供了一套通用的機(jī)制來(lái)管理同步狀態(tài)在塔、阻塞/喚醒線程、管理等待隊(duì)列,是JUC上大多數(shù)同步器的基礎(chǔ):
1拨黔、模板模式:定義一個(gè)操作中算法的骨架蛔溃,將一些步驟延遲到子類中。
2篱蝇、模板方法:AQS的一組模板方法贺待,直接調(diào)用同步方法組件
模板方法使得子類可以不改變算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟
3、同步組件:程序員需要選擇覆蓋實(shí)現(xiàn)以下方法來(lái)時(shí)下能同步狀態(tài)的管理
4态兴、同步狀態(tài):同步狀態(tài)的管理配合同步組件使用
CLH同步隊(duì)列是一個(gè)FIFO雙向隊(duì)列狠持,AQS依賴它來(lái)完成同步狀態(tài)的管理,在CLH同步隊(duì)列中瞻润,一個(gè)節(jié)點(diǎn)表示一個(gè)線程
總結(jié):線程會(huì)首先嘗試獲取鎖喘垂,失敗則將當(dāng)前線程以及等待狀態(tài)等信息包成一個(gè)Node節(jié)點(diǎn)加到CLH隊(duì)列來(lái)管理鎖,接著通過死循環(huán)嘗試獲取鎖绍撞,在拿到鎖為止會(huì)不斷阻塞正勒,等到線程釋放鎖時(shí),會(huì)喚醒隊(duì)列中的后繼線程
七傻铣、ReentrantLock
語(yǔ)法:ReentrantLock(boolean fair) 創(chuàng)建一個(gè)(公平/非公平)的具有可重入性質(zhì)的對(duì)象
1章贞、非公平(默認(rèn))
(1)通過CAS加鎖,如果成功則setExclusiveOwnerThread(設(shè)置獨(dú)占鎖)非洲;如果不成功代表已經(jīng)持有鎖是一個(gè)重入的線程
(2)acquire為AbstractQueuedSynchronizer的模塊方法鸭限,獲取線程并調(diào)用compareAndSetState進(jìn)行設(shè)置(加鎖)蜕径,加鎖失敗代表鎖已經(jīng)被占用,則判斷當(dāng)前線程是否是鎖的持有者败京,如果是則代表是一把重入鎖兜喻,通過setState不斷累加鎖重入的次數(shù)。
如果當(dāng)前線程不是鎖的持有者赡麦,拋出異常朴皆,鎖紀(jì)錄為0代表釋放全部,返回true,否則設(shè)置同步狀態(tài)返回false
2泛粹、公平鎖
調(diào)用hasQueuedPredecessors判斷:當(dāng)前線程不是同步隊(duì)列有其他線程優(yōu)先則返回false不能獲取鎖
八遂铡、ReentrantReadWriteLock
ReentrantReadWriteLock為讀寫鎖,對(duì)于讀操作是共享鎖晶姊,對(duì)于寫操作是獨(dú)占鎖
讀寫鎖是依賴AQS框架實(shí)現(xiàn)的共享鎖與排它鎖扒接,AQS 維護(hù)一個(gè)state是32位的,內(nèi)部類Sync采用一套運(yùn)算規(guī)則们衙,實(shí)現(xiàn)了高位保存共享鎖珠增,低位保存獨(dú)占鎖的一套邏輯
源碼大家感興趣可以自己去看看
實(shí)例:
結(jié)果運(yùn)行的效果是:先每隔 一秒打印一次?ti,說明寫寫互斥(每隔1s打涌嘲)蒂教,寫讀互斥(打印時(shí)不輸出讀操作的輸出),如果把兩個(gè)循環(huán)交換順序脆荷,可以看到凝垛,程序先等一秒,然后一次性輸出十次數(shù)據(jù)蜓谋,接著再一秒一次打印?ti梦皮, 說明讀讀不互斥(一次性全輸出),讀寫互斥(讀操作的sleep 1s的過程中桃焕,并沒有輸出寫操作的輸出)剑肯。
九、LockSupport
Object類的wait/notify機(jī)制相比观堂,park/unpark有兩個(gè)優(yōu)點(diǎn):1. 以thread為操作對(duì)象更符合阻塞線程的直觀定義让网;2. 操作更精準(zhǔn),可以準(zhǔn)確地喚醒某一個(gè)線程
每個(gè)線程都有一個(gè)許可(permit)师痕,permit只有兩個(gè)值1和0,默認(rèn)是0,通過park/unpark設(shè)置
補(bǔ)充知識(shí):為什么已經(jīng)有ReentrantReadWriteLock,jdk1.8還要引入StampedLock?
JDK1.8引入StampedLock溃睹,解決如果一直有讀操作導(dǎo)致寫操作饑餓的問題。
讀操作一直都能搶占到CPU時(shí)間片胰坟,而寫操作一直搶不了因篇,可能導(dǎo)致寫的饑餓問題,正因?yàn)镽eentrantReadWriteLock出現(xiàn)了讀和寫是互斥的情況,這個(gè)地方需要優(yōu)化竞滓,因此就出現(xiàn)了StampedLock
tryOptimisticRead():當(dāng)前沒有線程持有寫鎖咐吼,則簡(jiǎn)單的返回一個(gè)非 0 的 stamp 版本信息
validate():檢測(cè)樂觀讀版本號(hào)是否變化