溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

發(fā)布時(shí)間:2021-10-21 16:53:41 來源:億速云 閱讀:138 作者:iii 欄目:編程語言

本篇內(nèi)容介紹了“動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

前言碎語

    SynchronizedReentrantLock 大家應(yīng)該都不陌生了,作為java中最常用的本地鎖,最初版本中 ReentrantLock 的性能是遠(yuǎn)遠(yuǎn)強(qiáng)于 Synchronized 的,后續(xù)java在一次次的版本迭代中 對(duì) Synchronized 進(jìn)行了大量的優(yōu)化,直到 jdk1.6 之后,兩種鎖的性能已經(jīng)相差無幾,甚至 Synchronized 的自動(dòng)釋放鎖會(huì)更好用。

   在面試時(shí)被問到 Synchronized 和 ReentrantLock 的使用選擇時(shí),很多朋友都脫口而出的說用 Synchronized ,甚至在我面試的時(shí)候問面試者,也很少有人能夠答出所以然來,moon 想說,這可不一定,只對(duì)標(biāo)題感興趣的同學(xué)可以直接劃到最后,我可不是標(biāo)題黨~

Synchronized使用

   在 java 代碼中 synchronized 的使用是非常簡(jiǎn)單的

  • 1.直接貼在方法上

  • 2.貼在代碼塊兒上 動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

程序運(yùn)行期間,Synchronized那一塊兒代碼發(fā)生么什么?

   來看一張圖

動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

   在多線程運(yùn)行過程中,線程會(huì)去先搶對(duì)象的監(jiān)視器,這個(gè)監(jiān)視器是對(duì)象獨(dú)有的,其實(shí)就相當(dāng)于一把鑰匙,搶到了,那你就獲得了當(dāng)前代碼塊兒的執(zhí)行權(quán)。

   其他沒有搶到的線程會(huì)進(jìn)入隊(duì)列(SynchronizedQueue)當(dāng)中等待,等待當(dāng)前線程執(zhí)行完后,釋放鎖.

   最后當(dāng)前線程執(zhí)行完畢后通知出隊(duì)然后繼續(xù)重復(fù)當(dāng)前過程.

   從 jvm 的角度來看 monitorenter 和 monitorexit 指令代表著代碼的執(zhí)行與結(jié)束

SynchronizedQueue:

   SynchronizedQueue 是一個(gè)比較特殊的隊(duì)列,它沒有存儲(chǔ)功能,它的功能就是維護(hù)一組線程,其中每個(gè)插入操作必須等待另一個(gè)線程的移除操作,同樣任何一個(gè)移除操作都等待另一個(gè)線程的插入操作。因此此隊(duì)列內(nèi)部其 實(shí)沒有任何一個(gè)元素,或者說容量是0,嚴(yán)格說并不是一種容器。由于隊(duì)列沒有容量,因此不能調(diào)用 peek 操作,因?yàn)橹挥幸瞥貢r(shí)才有元素。

舉個(gè)例子:

   喝酒的時(shí)候,先把酒倒入酒盅,然后再倒入酒杯,這就是正常的隊(duì)列

   喝酒的時(shí)候,把酒直接倒入酒杯,這就是 SynchronizedQueue 。

   這個(gè)例子應(yīng)該很清晰易懂了,它的好處就是可以直接傳遞,省去了一個(gè)第三方傳遞的過程。

聊聊細(xì)節(jié),鎖升級(jí)的過程

   在 jdk1.6 以前,Synchronized 是一個(gè)重量級(jí)鎖,還是先貼一張圖

動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

   這就是為什么說,Synchronized 是一個(gè)重量級(jí)鎖的原因,因?yàn)槊恳淮捂i的資源都是直接和 cpu 去申請(qǐng)的,而 cpu 的鎖數(shù)量是固定的,當(dāng) cpu 鎖資源使用完后還會(huì)進(jìn)行鎖等待,這是一個(gè)非常耗時(shí)的操作。

   但是在jdk1.6,針對(duì)代碼層面進(jìn)行了大量的優(yōu)化,也就是我們常說的鎖升級(jí)的過程。

動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

這就是一個(gè)鎖升級(jí)的過程,我們簡(jiǎn)單的說說:

  • 無鎖:對(duì)象一開始就是無鎖狀態(tài)。

  • 偏向鎖:相當(dāng)于給對(duì)象貼了一個(gè)標(biāo)簽(將自己的線程 id 存入對(duì)象頭中),下次我再進(jìn)來時(shí),發(fā)現(xiàn)標(biāo)簽是我的,我就可以繼續(xù)使用了。

  • 自旋鎖:想象一下有一個(gè)廁所,里面有一個(gè)人在,你很想上但是只有一個(gè)坑位,所以你只能徘徊等待,等那個(gè)人出來以后,你就可以使用了。 這個(gè)自旋是使用 cas 來保證原子性的,關(guān)于 cas 我這里就不再贅述了。

  • 重量級(jí)鎖:直接向 cpu 去申請(qǐng)申請(qǐng)鎖,其他的線程都進(jìn)入隊(duì)列中等待。

鎖升級(jí)是什么時(shí)候發(fā)生的?

  • 偏向鎖:一個(gè)線程獲取鎖時(shí)會(huì)由無鎖升級(jí)為偏向鎖

  • 自旋鎖:當(dāng)產(chǎn)生線程競(jìng)爭(zhēng)時(shí)由偏向鎖升級(jí)為自旋鎖,想象一下 while(true) ;

  • 重量級(jí)鎖:當(dāng)線程競(jìng)爭(zhēng)到達(dá)一定數(shù)量或超過一定時(shí)間時(shí),晉升為重量級(jí)鎖

鎖的信息是記錄在哪里的?

動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

   這張圖是對(duì)象頭中 markword 的數(shù)據(jù)結(jié)構(gòu),鎖的信息就是在這里存放的,很清楚的表明了鎖在升級(jí)的時(shí)候鎖信息的變動(dòng),其實(shí)就是通過二進(jìn)制的數(shù)值,來對(duì)對(duì)象進(jìn)行一個(gè)標(biāo)記,每個(gè)數(shù)值代表一種狀態(tài)。

既然synchronized有鎖升級(jí)那么有鎖降級(jí)嗎?

   這個(gè)問題和我們的題目就有很大的關(guān)聯(lián)了。

   在 HotSpot 虛擬機(jī)中是有鎖降級(jí)的,但是僅僅只發(fā)生在 STW 的時(shí)候,只有垃圾回收線程能夠觀測(cè)到它,也就是說,在我們正常使用的過程中是不會(huì)發(fā)生鎖降級(jí)的,只有在 GC 的時(shí)候才會(huì)降級(jí)。

   所以題目的答案,你懂了嗎?哈哈,我們接著往下走。

ReentrantLock的使用

動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

   ReentrantLock 的使用也是非常簡(jiǎn)單的,與 Synchronized 的不同就是需要自己去手動(dòng)釋放鎖,為了保證一定釋放,所以通常都是和 try~finally 配合使用的。

ReentrantLock的原理

   ReentrantLock 意為可重入鎖,說起 ReentrantLock 就不得不說 AQS ,因?yàn)槠?strong>底層就是使用 AQS 去實(shí)現(xiàn)的。

   ReentrantLock有兩種模式,一種是公平鎖,一種是非公平鎖。

  • 公平模式下等待線程入隊(duì)列后會(huì)嚴(yán)格按照隊(duì)列順序去執(zhí)行

  • 非公平模式下等待線程入隊(duì)列后有可能會(huì)出現(xiàn)插隊(duì)情況

動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

   這就是ReentrantLock的結(jié)構(gòu)圖,我們看這張圖其實(shí)是很簡(jiǎn)單的,因?yàn)橹饕膶?shí)現(xiàn)都交給AQS去做了,我們下面著重聊一下AQS。

AQS

   AQS(AbstractQueuedSynchronizer): AQS 可以理解為就是一個(gè)可以實(shí)現(xiàn)鎖的框架

   簡(jiǎn)單的流程理解

   公平鎖:

動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

  • 第一步:獲取狀態(tài)的 state 的值。

    • 如果 state=0 即代表鎖沒有被其它線程占用,執(zhí)行第二步。

    • 如果 state!=0 則代表鎖正在被其它線程占用,執(zhí)行第三步。

  • 第二步:判斷隊(duì)列中是否有線程在排隊(duì)等待。

    • 如果不存在則直接將鎖的所有者設(shè)置成當(dāng)前線程,且更新狀態(tài) state 。

    • 如果存在就入隊(duì)。

  • 第三步:判斷鎖的所有者是不是當(dāng)前線程。

    • 如果是則更新狀態(tài) state 的值。

    • 如果不是,線程進(jìn)入隊(duì)列排隊(duì)等待。

   非公平鎖:

動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

  • 第一步:獲取狀態(tài)的 state 的值。

    • 如果 state=0 即代表鎖沒有被其它線程占用,則設(shè)置當(dāng)前鎖的持有者為當(dāng)前線程,該操作用 CAS 完成。

    • 如果不為0或者設(shè)置失敗,代表鎖被占用進(jìn)行下一步。

  • 此時(shí)獲取 state 的值,

    • 如果是,則給state+1,獲取鎖

    • 如果不是,則進(jìn)入隊(duì)列等待

    • 如果是0,代表剛好線程釋放了鎖,此時(shí)將鎖的持有者設(shè)為自己

    • 如果不是0,則查看線程持有者是不是自己

   讀完以上的部分相信你對(duì)AQS已經(jīng)有了一個(gè)比較清楚的概念了,所以我們來聊聊小細(xì)節(jié)。

   AQS使用state同步狀態(tài)(0代表無鎖,1代表有),并暴露出 getState 、 setState 以及 compareAndSet 操作來讀取和更新這個(gè)狀態(tài),使得僅當(dāng)同步狀態(tài)擁有一個(gè)期望值的時(shí)候,才會(huì)被原子地設(shè)置成新值。

   當(dāng)有線程獲取鎖失敗后,AQS是通過一個(gè)雙向的同步隊(duì)列來完成同步狀態(tài)的管理,就被添加到隊(duì)列末尾。

動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

   這是定義頭尾節(jié)點(diǎn)的代碼,我們可以先使用 volatile 去修飾的,就是保證讓其他線程可見,AQS 實(shí)際上就是修改頭尾兩個(gè)節(jié)點(diǎn)來完成入隊(duì)和出隊(duì)操作的。

   AQS 在鎖的獲取時(shí),并不一定只有一個(gè)線程才能持有這個(gè)鎖,所以此時(shí)有了獨(dú)占模式和共享模式的區(qū)別,我們本篇文章中的 ReentrantLock 使用的就是獨(dú)占模式,在多線程的情況下只會(huì)有一個(gè)線程獲取鎖。

動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

   獨(dú)占模式的流程是比較簡(jiǎn)單的,就根據(jù)state是否為0來判斷是否有線程已經(jīng)獲得了鎖,沒有就阻塞,有就繼續(xù)執(zhí)行后續(xù)代碼邏輯。

動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些

   共享模式的流程根據(jù)state是否大于0來判斷是否有線程已經(jīng)獲得了鎖,如果不大于0,就阻塞,如果大于0,通過CAS的原子操作來自減state的值,然后繼續(xù)執(zhí)行后續(xù)代碼邏輯。

ReentrantLock和Synchronized的區(qū)別

  • 其實(shí)ReentrantLock和Synchronized最核心的區(qū)別就在于Synchronized適合于并發(fā)競(jìng)爭(zhēng)低的情況,因?yàn)镾ynchronized的鎖升級(jí)如果最終升級(jí)為重量級(jí)鎖在使用的過程中是沒有辦法消除的,意味著每次都要和cpu去請(qǐng)求鎖資源,而ReentrantLock主要是提供了阻塞的能力,通過在高并發(fā)下線程的掛起,來減少競(jìng)爭(zhēng),提高并發(fā)能力,所以我們文章標(biāo)題的答案,也就顯而易見了。

  • synchronized是一個(gè)關(guān)鍵字,是由jvm層面去實(shí)現(xiàn)的,而ReentrantLock是由java api去實(shí)現(xiàn)的。

  • synchronized是隱式鎖,可以自動(dòng)釋放鎖,ReentrantLock是顯式鎖,需要手動(dòng)釋放鎖。

  • ReentrantLock可以讓等待鎖的線程響應(yīng)中斷,而synchronized卻不行,使用synchronized時(shí),等待的線程會(huì)一直等待下去,不能夠響應(yīng)中斷。

  • ReentrantLock可以獲取鎖狀態(tài),而synchronized不能。

說說標(biāo)題的答案

   其實(shí)題目的答案就在上一欄目的第一條,也是核心的區(qū)別,synchronized升級(jí)為重量級(jí)鎖后無法在正常情況下完成降級(jí),而ReentrantLock是通過阻塞來提高性能的,在設(shè)計(jì)模式上就體現(xiàn)出了對(duì)多線程情況的支持。

“動(dòng)態(tài)高并發(fā)時(shí)推薦ReentrantLock而不是Synchronized的原因有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI