摘要:通過(guò)關(guān)鍵字可以實(shí)現(xiàn)多線程之間的同步控制,除了上述方法,為我們提供了很多并發(fā)控制的工具類(lèi),今天主要講的就是中的重入鎖,效果基本等同于關(guān)鍵字。
在講重入鎖之前,我們先看一段代碼
上述代碼想要實(shí)現(xiàn)的效果,就是使用兩個(gè)線程對(duì)i分別進(jìn)行累加一百萬(wàn)次,最終希望i的值是二百萬(wàn),如果按照上述代碼運(yùn)行程序,你會(huì)發(fā)現(xiàn)i的值在絕大多數(shù)情況下都不能達(dá)到200萬(wàn),原因就是多線程的數(shù)據(jù)同步問(wèn)題。
為了解決上述問(wèn)題,我們自然而然想到synchronized關(guān)鍵字,通過(guò)對(duì)程序進(jìn)行簡(jiǎn)單改造,如下圖,紅框中的部分就是程序變動(dòng)的部分:
在此處synchronized關(guān)鍵字的作用就是,當(dāng)每個(gè)線程試圖對(duì)i進(jìn)行++操作時(shí),必須要先獲取o對(duì)象,一個(gè)o對(duì)象在同一時(shí)刻只能被一個(gè)線程所持有,其他線程必須要等待持有o對(duì)象的線程進(jìn)行i++操作并且釋放o對(duì)象之后去試圖獲取o對(duì)象,如果獲取成功線程繼續(xù)執(zhí)行,如果獲取失敗,線程繼續(xù)等待。通過(guò)synchronized關(guān)鍵字會(huì)使原本并行化的操作變成順序執(zhí)行,也就是說(shuō)同一時(shí)刻,只會(huì)有一個(gè)線程對(duì)i進(jìn)行++,因此i最終的值必定會(huì)是200萬(wàn)。
通過(guò)synchronized關(guān)鍵字可以實(shí)現(xiàn)多線程之間的同步控制,除了上述方法,Java為我們提供了很多并發(fā)控制的工具類(lèi),今天主要講的就是Java中的重入鎖ReentrantLock,效果基本等同于synchronized關(guān)鍵字。
使用重入鎖必須獲取一個(gè)重入鎖對(duì)象,通過(guò)new一個(gè)ReentrantLock即可獲得一個(gè)重入鎖對(duì)象。
使用重入鎖必須明確指定加鎖和解鎖操作,增強(qiáng)程序的可讀性。
同一把重入鎖只能在同一時(shí)刻只能被同一個(gè)線程鎖持有,也就是說(shuō),當(dāng)線程1通過(guò)lock方法獲取鎖成功之后,其他線程如果想要獲得鎖必須等待線程1通過(guò)unlock方法釋放鎖之后才能獲取成功。
重入鎖支持多次加鎖和多次解鎖操作,但是加鎖和解鎖的次數(shù)必須保持一致,如果一個(gè)線程的加鎖次數(shù)大于解鎖次數(shù),會(huì)使得當(dāng)前線程一直占有這把重入鎖,其他線程永遠(yuǎn)無(wú)法獲取鎖,從而產(chǎn)生饑餓現(xiàn)象,相反如果解鎖的次數(shù)大于加鎖次數(shù),程序則會(huì)拋出IllegalMonitorStateException異常。
重入鎖提供中斷響應(yīng),就是在等待鎖的過(guò)程中可以取消對(duì)鎖的請(qǐng)求。
通過(guò)圖片上的代碼,很輕松的就構(gòu)造了一個(gè)死鎖現(xiàn)象,當(dāng)lock值是1,線程會(huì)先試圖獲取重入鎖lock1,500ms之后再試圖獲取重入鎖lock2,相反如果lock值不是1,線程會(huì)先試圖獲取重入鎖lock2,500ms之后在試圖獲取重入鎖lock1,此時(shí),我在主函數(shù)中新開(kāi)兩個(gè)線程,設(shè)置lock的值一個(gè)為1,另一個(gè)為2;
此時(shí)運(yùn)行程序,你會(huì)發(fā)現(xiàn)程序永遠(yuǎn)不會(huì)結(jié)束,原因就是兩個(gè)線程之間形成了死鎖現(xiàn)象。
細(xì)心的讀者或許已經(jīng)發(fā)現(xiàn),我在獲取重入鎖的時(shí)候不是使用lock()方法,而是使用的lockInterruptibly()方法,通過(guò)方法名稱也可以看出,lockInterruptibly()方法是支持中斷響應(yīng)的。
下面我會(huì)在主線程通過(guò)t2.interrupt()中斷thread-2線程,這樣重入鎖2就會(huì)被釋放,從而使得thread-1可以正確執(zhí)行完畢,但是thread-2只是被中斷,無(wú)法正確執(zhí)行完畢,只會(huì)執(zhí)行finally塊中的方法,最終程序的輸出結(jié)果如下圖:
除了通過(guò)中斷線程我們還可以通過(guò)鎖申請(qǐng)等待限時(shí)來(lái)避免死鎖和饑餓現(xiàn)象,所謂的鎖申請(qǐng)等待限時(shí)指的是申請(qǐng)鎖時(shí)指定一個(gè)最大等待時(shí)間,如果超過(guò)了等待時(shí)間還沒(méi)獲得鎖,線程就不再進(jìn)行等待并且繼續(xù)執(zhí)行。
獲取鎖時(shí)使用tryLock方法來(lái)獲取鎖就可以,此方法會(huì)有一個(gè)boolean返回值,如果獲取鎖成功,返回值為true,如果失敗,返回值即為false。該方法有兩個(gè)重載方法,如下圖:
上述的實(shí)現(xiàn)方式都是非公平鎖,所謂的非公平就是,線程獲取鎖的成功率是隨機(jī)的,有些鎖可能會(huì)一直成功獲取鎖,而有些線程會(huì)一直獲取不到鎖,而那些獲取不到鎖的線程就會(huì)一直處于等待狀態(tài),從而產(chǎn)生饑餓現(xiàn)象。
為了解決上述問(wèn)題,重入鎖支持多個(gè)線程之間以一種公平的方式來(lái)競(jìng)爭(zhēng)獲取鎖,通俗一點(diǎn)講比如有兩個(gè)線程,兩個(gè)線程試圖獲取同一把鎖,假如說(shuō)第一次成功獲取鎖的是線程1,那么下次成功獲取鎖的必定是線程2而不是線程1。
公平重入鎖的實(shí)現(xiàn)只需要在獲取重入鎖時(shí),構(gòu)造參數(shù)中指定true。
上述代碼通過(guò)主線程中新開(kāi)兩個(gè)線程,每個(gè)線程所做的事就是循環(huán)的獲取fairLock這把重入鎖,由于fairLock是一把公平的重入鎖,因此t1和t2兩個(gè)線程會(huì)交替獲得鎖,程序運(yùn)行效果圖如下圖:
雖然公平的重入鎖可以避免死鎖的現(xiàn)象,但因內(nèi)部必須要維護(hù)一個(gè)有序的線程隊(duì)列,所以公平鎖的實(shí)現(xiàn)成本較高,性能相對(duì)低下。
最后附上上述實(shí)例代碼的地址:https://github.com/Meikoheng/...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/70859.html
摘要:解決上問(wèn)題在變量前添加版本號(hào),將變成循環(huán)時(shí)間長(zhǎng)開(kāi)銷(xiāo)大,因?yàn)樽孕枰闹荒鼙WC一個(gè)共享變量的原子操作分類(lèi)二重入鎖支持重進(jìn)入的鎖,排它鎖分類(lèi)三讀寫(xiě)鎖一對(duì)鎖,讀鎖,寫(xiě)鎖,在同一時(shí)刻允許多線程訪問(wèn) 1、 分類(lèi)一:樂(lè)觀鎖與悲觀鎖 a)悲觀鎖:認(rèn)為其他線程會(huì)干擾本身線程操作,所以加鎖 i.具體表現(xiàn)形式:synchronized關(guān)鍵字和lock實(shí)現(xiàn)類(lèi) ...
摘要:為什么叫重入鎖呢,我們把它拆開(kāi)來(lái)看就明了了。釋放鎖,每次鎖持有者數(shù)量遞減,直到為止。 相信大家在工作或者面試過(guò)程中經(jīng)常聽(tīng)到重入鎖這個(gè)概念,或者與關(guān)鍵字 synchrozied 的對(duì)比,棧長(zhǎng)面試了這么多人,80%的面試者都沒(méi)有答對(duì)或沒(méi)有答到點(diǎn)上,或者把雙重效驗(yàn)鎖搞混了,哭笑不得。。 那么你對(duì)重入鎖了解有多少呢?今天,棧長(zhǎng)幫大家撕開(kāi)重入鎖的面紗,來(lái)見(jiàn)識(shí)下重入鎖的真實(shí)容顏。。 什么是重入鎖 ...
摘要:公平鎖非公平鎖公平鎖公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖。加鎖后,任何其他試圖再次加鎖的線程會(huì)被阻塞,直到當(dāng)前進(jìn)程解鎖。重量級(jí)鎖會(huì)讓其他申請(qǐng)的線程進(jìn)入阻塞,性能降低。 Java 中15種鎖的介紹 在讀很多并發(fā)文章中,會(huì)提及各種各樣鎖如公平鎖,樂(lè)觀鎖等等,這篇文章介紹各種鎖的分類(lèi)。介紹的內(nèi)容如下: 公平鎖 / 非公平鎖 可重入鎖 / 不可重入鎖 獨(dú)享鎖 / 共享鎖 互斥鎖 / 讀...
摘要:很吧判斷是否有前驅(qū)線程等待獲取鎖公平所和非公平鎖的各自優(yōu)勢(shì)是什么那公平鎖很好理解,可以防止出現(xiàn)線程饑餓現(xiàn)象,每一個(gè)線程都有機(jī)會(huì)獲取到鎖。非公平鎖可能會(huì)導(dǎo)致線程饑餓,但是我們一般使用非公平鎖,因?yàn)榉枪芥i可以減少上下文的切換,提高效率。 鎖的重入是指同一個(gè)線程可以多次獲取同一個(gè)鎖,synchronize是隱式的可重入鎖,ReentrantLock通過(guò)代碼實(shí)現(xiàn)了鎖的重入: fina...
摘要:所謂的重入,就是當(dāng)本線程想再次獲得鎖,不需要重新申請(qǐng),它本身就已經(jīng)鎖了,即重入該鎖。如果不為,則表示有線程已經(jīng)占有了。總結(jié)回顧下要點(diǎn)是一個(gè)可重入的鎖被當(dāng)前占用的線程重入。 上一章《AQS源碼閱讀》講了AQS框架,這次講講它的應(yīng)用類(lèi)(注意不是子類(lèi)實(shí)現(xiàn),待會(huì)細(xì)講)。ReentrantLock,顧名思義重入鎖,但什么是重入,這個(gè)鎖到底是怎樣的,我們來(lái)看看類(lèi)的注解說(shuō)明showImg(http:...
閱讀 908·2021-09-03 10:42
閱讀 1518·2019-08-30 15:56
閱讀 1453·2019-08-29 17:27
閱讀 879·2019-08-29 15:25
閱讀 3166·2019-08-26 18:27
閱讀 2487·2019-08-26 13:41
閱讀 1895·2019-08-26 10:39
閱讀 1585·2019-08-23 18:36