總結(jié)java多線程之互斥與同步解決方案
一、線程互斥與同步
互斥:指的是多個線程不能同時訪問共享變量
同步:指的是多個線程按指定的順序執(zhí)行操作
在同時有多個線程運行過程中,如何達到互斥和同步呢?
- 加鎖即可
在此使用黑馬筆記中room例子來說明鎖。(ps: 以前就了解鎖,但總會記亂,發(fā)現(xiàn)使用形象化記憶后就很清楚)
解決互斥
- 鎖就相當(dāng)于上圖的房子,里面放著會被并發(fā)訪問的共享變量
- 此時綠色區(qū)域(owner)無線程,此時多個線程想并發(fā)訪問房子里的共享變量,那么只允許其中一個線程進入房子訪問,并把房門鎖上。
- 剩下的沒有拿到鎖的線程只能在entrylist中排隊
- owner中的線程訪問結(jié)束后會離開房子,并告訴entrylist的線程可以進房子了
- entrylist的線程開始新一輪的掙鎖,如此反復(fù)
- 這樣就能解決互斥的問題
解決同步
(這涉及到為什么wait(),notify()方法需要用鎖,就是因為只有用了鎖才能完成同步,那么怎么完成的呢?)
- 多個線程同時啟動,如果希望B線程在A線程之后執(zhí)行
- 那么當(dāng)B先搶到鎖,即先進入了房子,此時A只能在entrylist中排隊
- 為了讓A先執(zhí)行,那么可以先讓B進入藍色區(qū)域,即waitset中等待,并且把門打開,告訴entrylist中的線程可以進來了
- 那么A進來后,執(zhí)行完任務(wù),臨走時通知waitset中的B,B再回到綠色區(qū)域執(zhí)行任務(wù)就能保證有序了
- 這樣就能解決同步問題
那么room這個數(shù)據(jù)結(jié)構(gòu)其實就是synchronized的核心了,接下來總結(jié)synchronized原理的時候會一直用room的例子
二、synchronized
很多人對synchronized原理的理解也就停留在知道字節(jié)碼有個monitor關(guān)鍵字來管理鎖,再淺一點的只知道怎么用,再者懂得深一點的卻記不住。我之前就是想深入了解一下但覺得苦澀,就看不下去了,看了黑馬的筆記我覺得這玩意兒其實很簡單,所以好的老師還是比較重要的。那么在此我也記錄一下怎么更好的去理解synchronized的底層原理
從字節(jié)碼我們可以知道synchronized的底層就是關(guān)聯(lián)了一個monitor,那么這玩意兒是個什么東西,怎么實現(xiàn)鎖的功能呢?
首先,可以把monitor的數(shù)據(jù)結(jié)構(gòu)簡化成上圖的room,具體點描述如下圖
- synchronized(鎖對象)的時候,相當(dāng)于讓鎖對象綁定了一個monitor(具體綁定方法不打算在后面總結(jié))
- 那么多個線程中方法涉及到該鎖對象時,都會來訪問鎖對象對應(yīng)的monitor
- 此時線程thread-2搶到了鎖,操作就是讓monitor中的owner字段指向thread-2線程,意味著當(dāng)前線程獲取到了基于該monitor的鎖
- 其他沒搶到鎖的,monitor會將他們放在Entrylist中等待,這些線程只能在隊列中等著
- thread-2線程完成操作后就會退出,并通知entrylist的線程重新?lián)屾i
- 如果在執(zhí)行過程中,線程調(diào)用了wait()方法,monitor就會將他們放入waitset中等待別人喚醒
- (看回room結(jié)構(gòu))owner進入waitset后會把門打開,讓entrylist的線程進來
- 直到某時刻owner中有線程調(diào)用notify()方法,waitset中的線程才會被喚醒,喚醒后會進入entrylist中重新?lián)屾i
以上就是synchronized的原理。有人就會問了,你說的這些文字我都懂啊,搞個圖擺在這也沒啥用。
接下來我將從上圖直接回答下面的常見的問題
wait()和notify()為什么都得在synchronized后使用?
- wait()就是將線程放入waitset中,那么waitset是在room里面的,不上鎖怎么能進room中?同理,不進入room,在門外怎么使用notify()怎么能叫醒waitset中的線程?
wait()會釋放鎖嗎?
- 廢話,不開門的話,怎么放線程進來,就更別提喚醒了
notifyALL()為什么不會喚醒其他鎖對象的線程?
- 進哪個room才能叫哪個waitset,進了Aroom當(dāng)然只能叫醒A的waiset了
說說synchronized的原理?
- 把圖畫出來就行了
線程什么時候從runnable變成waiting,什么時候變成block?
- 看圖,進入waiset就是wait,所以調(diào)wait()就變成waiting狀態(tài)進入entrylist就是block,所以被喚醒后以及沒搶到鎖都變block 。。。。。。。。。。。。
注意了,這里涉及monitor的原理都是synchronized最根本的原理,也稱重量級鎖,可以看到monitor會頻繁切換線程狀態(tài),效率比較低。后來synchronized改進了,在使用monitor前還有好幾種方案,分別為偏向鎖,輕量鎖,以及自旋優(yōu)化。這部分也是面試??键c,也容易記亂,但用圖例去記就很清楚。
那么接下來就說說synchronized的改進
三、輕量鎖與偏向鎖
輕量鎖與偏向鎖的核心都是先不讓線程沖突的時候直接去找monitor,而是先用鎖對象的對象頭字段來解決沖突
(寫博客好累啊。。。算了我就總結(jié)一些自己覺得關(guān)鍵的地方吧)
輕量鎖
- 對于輕量鎖而言,每個線程維護了一個鎖記錄,搶占鎖的過程就是用CAS將自己的信息與鎖對象的對象頭mark word部分交換
- 這樣其他慢一步的線程CAS會失敗,就意識到鎖已經(jīng)被占了
- 可重入只需要在占鎖的時候判斷鎖對象的markword記錄的是不是自己的線程id即可,是的話就能夠獲取鎖,也就是疊加一個鎖記錄
- 釋放鎖就意味著刪除鎖記錄,直到鎖記錄清空,就將鎖對象頭部被修改的字段變回原樣
- 輕量鎖是認為不會有競爭,如果發(fā)生了線程競爭,鎖需要升級,不然上述方法沒有像monitor的entrylist來管理其他競爭暫時沒拿到鎖的線程
- 鎖升級就是鎖膨脹,直接調(diào)monitor來管理,就將owner指向當(dāng)前線程,然后競爭線程去entrylist排隊
- 其中涉及自旋優(yōu)化,就是線程競爭時,第二個線程不用立刻去entrylist中,這樣又要涉及上下文切換,可以自旋一會看鎖能否搶到
偏向鎖
- 輕量鎖每次占鎖都要用一次CAS來更新鎖對象頭,如果本來就沒啥競爭那CAS就是無用的操作了
- 為了解決這個問題,線程搶鎖成功后直接把自己的ID刻在鎖對象頭中,需要判斷重入時只需判斷ID是否相同即可
到此這篇關(guān)于總結(jié)java多線程之互斥與同步解決方案的文章就介紹到這了,更多相關(guān)java多線程之互斥與同步內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis-Plus中update()和updateById()將字段更新為null
本文主要介紹了Mybatis-Plus中update()和updateById()將字段更新為null,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08SpringCloud服務(wù)接口調(diào)用OpenFeign及使用詳解
這篇文章主要介紹了SpringCloud服務(wù)接口調(diào)用——OpenFeign,在學(xué)習(xí)Ribbon時,服務(wù)間調(diào)用使用的是RestTemplate+Ribbon實現(xiàn),而Feign在此基礎(chǔ)上繼續(xù)進行了封裝,使服務(wù)間調(diào)用變得更加方便,需要的朋友可以參考下2023-04-04Java獲取當(dāng)?shù)氐娜粘鋈章鋾r間代碼分享
這篇文章主要介紹了Java獲取當(dāng)?shù)氐娜粘鋈章鋾r間代碼分享,國外猿友寫的一個類,需要的朋友可以參考下2014-06-06Mybatis報Type interface *.*Mapper is not&
本文主要介紹了Mybatis報Type interface *.*Mapper is not known to the MapperRegis,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07詳解Spring-bean的循環(huán)依賴以及解決方式
這篇文章主要介紹了詳解Spring-bean的循環(huán)依賴以及解決方式,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-09-09Spring中實現(xiàn)定時調(diào)度的幾種方法
本篇文章主要介紹了Spring中實現(xiàn)定時調(diào)度示例,可以在無人值守的時候系統(tǒng)可以在某一時刻執(zhí)行某些特定的功能,有興趣的可以了解一下。2017-02-02