總結(jié)鎖的升級流程
重量級鎖
重量級鎖依賴于操作系統(tǒng)的互斥量(mutex) 實(shí)現(xiàn)的,而操作系統(tǒng)中線程間狀態(tài)的轉(zhuǎn)換需要相對比較長的時(shí)間,所以重量級鎖效率很低,但被阻塞的線程不會消耗CPU。
前面說到,每一個(gè)對象都可以當(dāng)做一個(gè)鎖,當(dāng)多個(gè)線程同時(shí)請求某個(gè)對象鎖時(shí),對象鎖會設(shè)置幾種狀態(tài)用來區(qū)分請求的線程:
Contention List:所有請求鎖的線程將被首先放置到該競爭隊(duì)列 Entry List:Contention List中那些有資格成為候選人的線程被移到Entry List Wait Set:那些調(diào)用wait方法被阻塞的線程被放置到Wait Set OnDeck:任何時(shí)刻最多只能有一個(gè)線程正在競爭鎖,該線程稱為OnDeck Owner:獲得鎖的線程稱為Owner !Owner:釋放鎖的線程
當(dāng)一個(gè)線程嘗試獲得鎖時(shí),如果該鎖已經(jīng)被占用,則會將該線程封裝成一個(gè)ObjectWaiter對象插入到Contention List的隊(duì)列的隊(duì)首,然后調(diào)用park函數(shù)掛起當(dāng)前線程。
當(dāng)線程釋放鎖時(shí),會從Contention List或EntryList中挑選一個(gè)線程喚醒,被選中的線程叫做Heir presumptive即假定繼承人,假定繼承人被喚醒后會嘗試獲得鎖,但synchronized是非公平的,所以假定繼承人不一定能獲得鎖。這是因?yàn)閷τ谥亓考夋i,線程先自旋嘗試獲得鎖,這樣做的目的是為了減少執(zhí)行操作系統(tǒng)同步操作帶來的開銷。如果自旋不成功再進(jìn)入等待隊(duì)列。這對那些已經(jīng)在等待隊(duì)列中的線程來說,稍微顯得不公平,還有一個(gè)不公平的地方是自旋線程可能會搶占了Ready線程的鎖。
如果線程獲得鎖后調(diào)用Object.wait方法,則會將線程加入到WaitSet中,當(dāng)被Object.notify喚醒后,會將線程從WaitSet移動到Contention List或EntryList中去。需要注意的是,當(dāng)調(diào)用一個(gè)鎖對象的wait或notify方法時(shí),如當(dāng)前鎖的狀態(tài)是偏向鎖或輕量級鎖則會先膨脹成重量級鎖。
總結(jié)鎖的升級流程
每一個(gè)線程在準(zhǔn)備獲取共享資源時(shí): 第一步,檢查MarkWord里面是不是放的自己的ThreadId ,如果是,表示當(dāng)前線程是處于 “偏向鎖” 。
第二步,如果MarkWord不是自己的ThreadId,鎖升級,這時(shí)候,用CAS來執(zhí)行切換,新的線程根據(jù)MarkWord里面現(xiàn)有的ThreadId,通知之前線程暫停,之前線程將Markword的內(nèi)容置為空。
第三步,兩個(gè)線程都把鎖對象的HashCode復(fù)制到自己新建的用于存儲鎖的記錄空間,接著開始通過CAS操作, 把鎖對象的MarKword的內(nèi)容修改為自己新建的記錄空間的地址的方式競爭MarkWord。
第四步,第三步中成功執(zhí)行CAS的獲得資源,失敗的則進(jìn)入自旋 。
第五步,自旋的線程在自旋過程中,成功獲得資源(即之前獲的資源的線程執(zhí)行完成并釋放了共享資源),則整個(gè)狀態(tài)依然處于 輕量級鎖的狀態(tài),如果自旋失敗 。
第六步,進(jìn)入重量級鎖的狀態(tài),這個(gè)時(shí)候,自旋的線程進(jìn)行阻塞,等待之前線程執(zhí)行完成并喚醒自己。
各種鎖的優(yōu)缺點(diǎn)對比
下表來自《Java并發(fā)編程的藝術(shù)》:
Java 任務(wù)調(diào)度 容器
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。