這篇文章將為大家詳細(xì)講解有關(guān)Java鎖中的重入鎖該怎么理解,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。
創(chuàng)新互聯(lián)建站制作網(wǎng)站網(wǎng)頁找三站合一網(wǎng)站制作公司,專注于網(wǎng)頁設(shè)計(jì),成都做網(wǎng)站、網(wǎng)站制作,網(wǎng)站設(shè)計(jì),企業(yè)網(wǎng)站搭建,網(wǎng)站開發(fā),建網(wǎng)站業(yè)務(wù),680元做網(wǎng)站,已為1000+服務(wù),創(chuàng)新互聯(lián)建站網(wǎng)站建設(shè)將一如既往的為我們的客戶提供最優(yōu)質(zhì)的網(wǎng)站建設(shè)、網(wǎng)絡(luò)營銷推廣服務(wù)!
在講重入鎖之前,我們先看一段代碼
上述代碼想要實(shí)現(xiàn)的效果,就是使用兩個(gè)線程對i分別進(jìn)行累加一百萬次,最終希望i的值是二百萬,如果按照上述代碼運(yùn)行程序,你會發(fā)現(xiàn)i的值在絕大多數(shù)情況下都不能達(dá)到200萬,原因就是多線程的數(shù)據(jù)同步問題。
為了解決上述問題,我們自然而然想到synchronized關(guān)鍵字,通過對程序進(jìn)行簡單改造,如下圖,紅框中的部分就是程序變動的部分:
在此處synchronized關(guān)鍵字的作用就是,當(dāng)每個(gè)線程試圖對i進(jìn)行++操作時(shí),必須要先獲取o對象,一個(gè)o對象在同一時(shí)刻只能被一個(gè)線程所持有,其他線程必須要等待持有o對象的線程進(jìn)行i++操作并且釋放o對象之后去試圖獲取o對象,如果獲取成功線程繼續(xù)執(zhí)行,如果獲取失敗,線程繼續(xù)等待。通過synchronized關(guān)鍵字會使原本并行化的操作變成順序執(zhí)行,也就是說同一時(shí)刻,只會有一個(gè)線程對i進(jìn)行++,因此i最終的值必定會是200萬。
通過synchronized關(guān)鍵字可以實(shí)現(xiàn)多線程之間的同步控制,除了上述方法,Java為我們提供了很多并發(fā)控制的工具類,今天主要講的就是Java中的重入鎖ReentrantLock,效果基本等同于synchronized關(guān)鍵字。
使用重入鎖必須獲取一個(gè)重入鎖對象,通過new一個(gè)ReentrantLock即可獲得一個(gè)重入鎖對象。
使用重入鎖必須明確指定加鎖和解鎖操作,增強(qiáng)程序的可讀性。
同一把重入鎖只能在同一時(shí)刻只能被同一個(gè)線程鎖持有,也就是說,當(dāng)線程1通過lock方法獲取鎖成功之后,其他線程如果想要獲得鎖必須等待線程1通過unlock方法釋放鎖之后才能獲取成功。
重入鎖支持多次加鎖和多次解鎖操作,但是加鎖和解鎖的次數(shù)必須保持一致,如果一個(gè)線程的加鎖次數(shù)大于解鎖次數(shù),會使得當(dāng)前線程一直占有這把重入鎖,其他線程永遠(yuǎn)無法獲取鎖,從而產(chǎn)生饑餓現(xiàn)象,相反如果解鎖的次數(shù)大于加鎖次數(shù),程序則會拋出IllegalMonitorStateException異常。
重入鎖提供中斷響應(yīng),就是在等待鎖的過程中可以取消對鎖的請求。
通過圖片上的代碼,很輕松的就構(gòu)造了一個(gè)死鎖現(xiàn)象,當(dāng)lock值是1,線程會先試圖獲取重入鎖lock1,500ms之后再試圖獲取重入鎖lock2,相反如果lock值不是1,線程會先試圖獲取重入鎖lock2,500ms之后在試圖獲取重入鎖lock1,此時(shí),我在主函數(shù)中新開兩個(gè)線程,設(shè)置lock的值一個(gè)為1,另一個(gè)為2;
此時(shí)運(yùn)行程序,你會發(fā)現(xiàn)程序永遠(yuǎn)不會結(jié)束,原因就是兩個(gè)線程之間形成了死鎖現(xiàn)象。
細(xì)心的讀者或許已經(jīng)發(fā)現(xiàn),我在獲取重入鎖的時(shí)候不是使用lock()方法,而是使用的lockInterruptibly()方法,通過方法名稱也可以看出,lockInterruptibly()方法是支持中斷響應(yīng)的。
下面我會在主線程通過t2.interrupt()中斷thread-2線程,這樣重入鎖2就會被釋放,從而使得thread-1可以正確執(zhí)行完畢,但是thread-2只是被中斷,無法正確執(zhí)行完畢,只會執(zhí)行finally塊中的方法,最終程序的輸出結(jié)果如下圖:
除了通過中斷線程我們還可以通過鎖申請等待限時(shí)來避免死鎖和饑餓現(xiàn)象,所謂的鎖申請等待限時(shí)指的是申請鎖時(shí)指定一個(gè)最大等待時(shí)間,如果超過了等待時(shí)間還沒獲得鎖,線程就不再進(jìn)行等待并且繼續(xù)執(zhí)行。
想要實(shí)現(xiàn)上述效果只需要在獲取鎖時(shí)使用tryLock方法來獲取鎖就可以,此方法會有一個(gè)boolean返回值,如果獲取鎖成功,返回值為true,如果失敗,返回值即為false。該方法有兩個(gè)重載方法,如下圖:
上述的實(shí)現(xiàn)方式都是非公平鎖,所謂的非公平就是,線程獲取鎖的成功率是隨機(jī)的,有些鎖可能會一直成功獲取鎖,而有些線程會一直獲取不到鎖,而那些獲取不到鎖的線程就會一直處于等待狀態(tài),從而產(chǎn)生饑餓現(xiàn)象。
為了解決上述問題,重入鎖支持多個(gè)線程之間以一種公平的方式來競爭獲取鎖,通俗一點(diǎn)講比如有兩個(gè)線程,兩個(gè)線程試圖獲取同一把鎖,假如說第一次成功獲取鎖的是線程1,那么下次成功獲取鎖的必定是線程2而不是線程1。
公平重入鎖的實(shí)現(xiàn)只需要在獲取重入鎖時(shí),構(gòu)造參數(shù)中指定true。
上述代碼通過主線程中新開兩個(gè)線程,每個(gè)線程所做的事就是循環(huán)的獲取fairLock這把重入鎖,由于fairLock是一把公平的重入鎖,因此t1和t2兩個(gè)線程會交替獲得鎖,程序運(yùn)行效果圖如下圖:
雖然公平的重入鎖可以避免死鎖的現(xiàn)象,但因內(nèi)部必須要維護(hù)一個(gè)有序的線程隊(duì)列,所以公平鎖的實(shí)現(xiàn)成本較高,性能相對低下。
關(guān)于Java鎖中的重入鎖該怎么理解就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。