欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入了解Java?Synchronized鎖升級(jí)過(guò)程

 更新時(shí)間:2022年03月10日 10:05:22   作者:SH的全棧筆記  
java中的鎖是針對(duì)對(duì)象而言的,它鎖住的是一個(gè)對(duì)象,并且具有可重入的性質(zhì),下面這篇文章主要給大家介紹了關(guān)于Java?Synchronized鎖升級(jí)過(guò)程的相關(guān)資料,需要的朋友可以參考下

前言

首先,synchronized 是什么?我們需要明確的給個(gè)定義——同步鎖,沒(méi)錯(cuò),它就是把。

可以用來(lái)干嘛?鎖,當(dāng)然當(dāng)然是用于線程間的同步,以及保護(hù)臨界區(qū)內(nèi)的資源。我們知道,鎖是個(gè)非?;\統(tǒng)的概念,像生活中有指紋鎖、密碼鎖等等多個(gè)種類(lèi),那 synchronized 代表的鎖具體是把什么鎖呢?

答案是—— Java 內(nèi)置鎖。在 Java 中,每個(gè)對(duì)象中都隱藏著一把鎖,而 synchronized 關(guān)鍵字就是激活這把隱式鎖的把手(開(kāi)關(guān))。

先來(lái)簡(jiǎn)單了解一下 synchronized,我們知道其共有 3 種使用方式:

  • 修飾靜態(tài)方法:鎖住當(dāng)前 class,作用于該 class 的所有實(shí)例
  • 修飾非靜態(tài)方法:只會(huì)鎖住當(dāng)前 class 的實(shí)例
  • 修飾代碼塊:該方法接受一個(gè)對(duì)象作為參數(shù),鎖住的即該對(duì)象

使用方法就不在這里贅述,可自行搜索其詳細(xì)的用法,這不是本篇文章所關(guān)心的內(nèi)容。

知道了 synchronized 的概念,回頭來(lái)看標(biāo)題,它說(shuō)的鎖升級(jí)到底是個(gè)啥?對(duì)于不太熟悉鎖升級(jí)的人來(lái)說(shuō),可能會(huì)想:

所謂鎖,不就是啪一下鎖上就完事了嗎?升級(jí)是個(gè)什么玩意?這跟打撲克牌也沒(méi)關(guān)系啊。

對(duì)于熟悉的人來(lái)說(shuō),可能會(huì)想:

不就是「無(wú)鎖 ==> 偏向鎖 ==> 輕量級(jí)鎖 ==> 重量級(jí)鎖 」嗎?

你可能在很多地方看到過(guò)上面描述的鎖升級(jí)過(guò)程,也能直接背下來(lái)。但你真的知道無(wú)鎖、偏向鎖輕量級(jí)鎖、重量級(jí)鎖到底代表著什么嗎?這些鎖存儲(chǔ)在哪里?以及什么情況下會(huì)使得鎖向下一個(gè) level 升級(jí)?

想知道答案,我們似乎必須先搞清楚 Java 內(nèi)置鎖,其內(nèi)部結(jié)構(gòu)是啥樣的??jī)?nèi)置鎖又存放在哪里?

答案在開(kāi)篇提到過(guò)——在 Java 對(duì)象中。

那么現(xiàn)在的問(wèn)題就從「內(nèi)置鎖結(jié)構(gòu)是啥」變成了「Java 對(duì)象長(zhǎng)啥樣」。

對(duì)象結(jié)構(gòu)

宏觀上看,Java 對(duì)象的結(jié)構(gòu)很簡(jiǎn)單,分為三部分:

微觀上看,各個(gè)部分都還可以深入展開(kāi),詳見(jiàn)下圖:

接下來(lái)分別深入討論一下這三部分。

對(duì)象頭

從腦圖中可以看出,其由 Mark Word、Class Pointer、數(shù)組長(zhǎng)度三個(gè)字段組成。簡(jiǎn)單來(lái)說(shuō):

  • Mark Word:主要用于存儲(chǔ)自身運(yùn)行時(shí)數(shù)據(jù)
  • Class Pointer:是指針,指向方法區(qū)中該 class 的對(duì)象,JVM 通過(guò)此字段來(lái)判斷當(dāng)前對(duì)象是哪個(gè)類(lèi)的實(shí)例
  • 數(shù)組長(zhǎng)度:當(dāng)且僅當(dāng)對(duì)象是數(shù)組時(shí)才會(huì)有該字段

Class Pointer 和數(shù)組長(zhǎng)度沒(méi)什么好說(shuō)的,接下來(lái)重點(diǎn)聊聊 Mark Word。

Mark Word 所代表的「運(yùn)行時(shí)數(shù)據(jù)」主要用來(lái)表示當(dāng)前 Java 對(duì)象的線程鎖狀態(tài)以及 GC 的標(biāo)志。而線程鎖狀態(tài)分別就是無(wú)鎖、偏向鎖、輕量級(jí)鎖、重量級(jí)鎖。

所以前文提到的這 4 個(gè)狀態(tài),其實(shí)就是 Java 內(nèi)置鎖的不同狀態(tài)。

在 JDK 1.6 之前,內(nèi)置鎖都是重量級(jí)鎖,效率低下。效率低下表現(xiàn)在

而在 JDK 1.6 之后為了提高 synchronized 的效率,才引入了偏向鎖、輕量級(jí)鎖。

隨著鎖競(jìng)爭(zhēng)逐漸激烈,其狀態(tài)會(huì)按照「無(wú)鎖 ==> 偏向鎖 ==> 輕量級(jí)鎖 ==> 重量級(jí)鎖 」這個(gè)方向逐漸升級(jí),并且不可逆,只能進(jìn)行鎖升級(jí),而無(wú)法進(jìn)行鎖降級(jí)。

接下來(lái)我們思考一個(gè)問(wèn)題,既然 Mark Word 可以表示 4 種不同的鎖狀態(tài),其內(nèi)部到底是怎么區(qū)分的呢?(由于目前主流的 JVM 都是 64 位,所以我們只討論 64 位的 Mark Word)接下來(lái)我們通過(guò)圖片直觀的感受一下。

(1)無(wú)鎖

這個(gè)可以理解為單線程很快樂(lè)的運(yùn)行,沒(méi)有其他的線程來(lái)和其競(jìng)爭(zhēng)。

(2)偏向鎖

首先,什么叫偏向鎖?舉個(gè)例子,一段同步的代碼,一直只被線程 A 訪問(wèn),既然沒(méi)有其他的線程來(lái)競(jìng)爭(zhēng),每次都要獲取鎖豈不是浪費(fèi)資源?所以這種情況下線程 A 就會(huì)自動(dòng)進(jìn)入偏向鎖的狀態(tài)。

后續(xù)線程 A 再次訪問(wèn)同步代碼時(shí),不需要做任何的 check,直接執(zhí)行(對(duì)該線程的「偏愛(ài)」),這樣降低了獲取鎖的代價(jià),提升了效率。

看到這里,你會(huì)發(fā)現(xiàn)無(wú)鎖、偏向鎖的 lock 標(biāo)志位是一樣的,即都是 01,這是因?yàn)闊o(wú)鎖、偏向鎖是靠字段 biased_lock 來(lái)區(qū)分的,0 代表沒(méi)有使用偏向鎖,1 代表啟用了偏向鎖。為什么要這么搞?你可以理解為無(wú)鎖、偏向鎖在本質(zhì)上都可以理解為無(wú)鎖(參考上面提到的線程 A 的狀態(tài)),所以 lock 的標(biāo)志位都是 01 是沒(méi)毛病的。

PS:這里的線程 ID 是持有當(dāng)前對(duì)象偏向鎖的線程

(3)輕量級(jí)鎖

但是,一旦有第二個(gè)線程參與競(jìng)爭(zhēng),就會(huì)立即膨脹為輕量級(jí)鎖。企圖搶占的線程一開(kāi)始會(huì)使用自旋

的方式去嘗試獲取鎖。如果循環(huán)幾次,其他的線程釋放了鎖,就不需要進(jìn)行用戶態(tài)到內(nèi)核態(tài)的切換。雖然如此,但自旋需要占用很多 CPU 的資源(自行理解汽車(chē)空檔瘋狂踩油門(mén))。如果另一個(gè)線程 一直不釋放鎖,難道它就在這一直空轉(zhuǎn)下去嗎?

當(dāng)然不可能,JDK 1.7 之前是普通自旋,會(huì)設(shè)定一個(gè)最大的自旋次數(shù),默認(rèn)是 10 次,超過(guò)這個(gè)閾值就停止自旋。JDK 1.7 之后,引入了適應(yīng)性自旋。簡(jiǎn)單來(lái)說(shuō)就是:這次自旋獲取到鎖了,自旋的次數(shù)就會(huì)增加;這次自旋沒(méi)拿到鎖,自旋的次數(shù)就會(huì)減少。

(4)重量級(jí)鎖

上面提到,試圖搶占的線程自旋達(dá)到閾值,就會(huì)停止自旋,那么此時(shí)鎖就會(huì)膨脹成重量級(jí)鎖。當(dāng)其膨脹成重量級(jí)鎖后,其他競(jìng)爭(zhēng)的線程進(jìn)來(lái)就不會(huì)自旋了,而是直接阻塞等待,并且 Mark Word 中的內(nèi)容會(huì)變成一個(gè)監(jiān)視器(monitor)對(duì)象,用來(lái)統(tǒng)一管理排隊(duì)的線程。

這個(gè) monitor 對(duì)象,每個(gè)對(duì)象都會(huì)關(guān)聯(lián)一個(gè)。monitor 對(duì)象本質(zhì)上是一個(gè)同步機(jī)制,保證了同時(shí)只有一個(gè)線程能夠進(jìn)入臨界區(qū),在 HotSpot 的虛擬機(jī)中,是由 C++ 類(lèi) ObjectMonitor 實(shí)現(xiàn)的。

那么 monitor 對(duì)象具體是如何來(lái)管理線程的?接下來(lái)我們看幾個(gè) ObjectMonitor 類(lèi)關(guān)鍵的屬性:

  • ContentionQueue:是個(gè)隊(duì)列,所有競(jìng)爭(zhēng)鎖的線程都會(huì)先進(jìn)入這個(gè)隊(duì)列中,可以理解為線程的統(tǒng)一入口,進(jìn)入的線程會(huì)阻塞。
  • EntryList:ContentionQueue 中有資格的線程會(huì)被移動(dòng)到這里,相當(dāng)于進(jìn)行一輪初篩,進(jìn)入的線程會(huì)阻塞。
  • Owner:擁有當(dāng)前 monitor 對(duì)象的線程,即 —— 持有鎖的那個(gè)線程。
  • OnDeck:與 Owner 線程進(jìn)行競(jìng)爭(zhēng)的線程,同一時(shí)刻只會(huì)有一個(gè) OnDeck 線程在競(jìng)爭(zhēng)。
  • WaitSet:當(dāng) Owner 線程調(diào)用 wait()  方法被阻塞之后,會(huì)被放到這里。當(dāng)其被喚醒之后,會(huì)重新進(jìn)入 EntryList 當(dāng)中,這個(gè)集合的線程都會(huì)阻塞。
  • Count:用于實(shí)現(xiàn)可重入鎖,synchronized 是可重入的。

對(duì)象體

對(duì)象體包含了當(dāng)前對(duì)象的字段和值,在業(yè)務(wù)中u l是較為核心的部分。

對(duì)齊字節(jié)

就是單純用于填充的字節(jié),沒(méi)有其他的業(yè)務(wù)含義。其目的是為了保證對(duì)象所占用的內(nèi)存大小為 8 的倍數(shù),因?yàn)镠otSpot VM 的內(nèi)存管理要求對(duì)象的起始地址必須是 8 的倍數(shù)。

鎖升級(jí)

了解完 4 種鎖狀態(tài)之后,我們就可以整體的來(lái)看一下鎖升級(jí)的過(guò)程了。

線程 A 進(jìn)入 synchronized 開(kāi)始搶鎖,JVM 會(huì)判斷當(dāng)前是否是偏向鎖的狀態(tài),如果是就會(huì)根據(jù) Mark Word 中存儲(chǔ)的線程 ID 來(lái)判斷,當(dāng)前線程 A 是否就是持有偏向鎖的線程。如果是,則忽略 check,線程 A 直接執(zhí)行臨界區(qū)內(nèi)的代碼。

但如果 Mark Word 里的線程不是線程 A,就會(huì)通過(guò)自旋嘗試獲取鎖,如果獲取到了,就將 Mark Word 中的線程 ID 改為自己的;如果競(jìng)爭(zhēng)失敗,就會(huì)立馬撤銷(xiāo)偏向鎖,膨脹為輕量級(jí)鎖。

后續(xù)的競(jìng)爭(zhēng)線程都會(huì)通過(guò)自旋來(lái)嘗試獲取鎖,如果自旋成功那么鎖的狀態(tài)仍然是輕量級(jí)鎖。然而如果競(jìng)爭(zhēng)失敗,鎖會(huì)膨脹為重量級(jí)鎖,后續(xù)等待的競(jìng)爭(zhēng)的線程都會(huì)被阻塞。

補(bǔ)充:Synchronized底層原理

Synchronized被編譯后會(huì)生成monitorenter 和 monitorexit 指令。

在執(zhí)行monitorenter時(shí),會(huì)嘗試獲取對(duì)象的鎖,如果鎖的計(jì)數(shù)器為 0 則表示鎖可以被獲取,獲取后將鎖計(jì)數(shù)器設(shè)為 1 也就是加 1。

在執(zhí)行 monitorexit 指令后,將鎖計(jì)數(shù)器設(shè)為 0,表明鎖被釋放。如果獲取對(duì)象鎖失敗,那當(dāng)前線程就要阻塞等待,直到鎖被另外一個(gè)線程釋放為止。

Monitor是依賴(lài)于操作系統(tǒng)的mutex lock實(shí)現(xiàn)的,每當(dāng)掛起或者喚醒1個(gè)線程都要切換操作系統(tǒng)內(nèi)核態(tài)。這個(gè)操作是比較重量級(jí)的,在一些情況下甚至切換時(shí)間本身會(huì)超出線程執(zhí)行任務(wù)的時(shí)間。

synchronized 修飾的方法并沒(méi)有 monitorenter 指令和 monitorexit 指令,取得代之的確實(shí)是 ACC_SYNCHRONIZED 標(biāo)識(shí),該標(biāo)識(shí)指明了該方法是一個(gè)同步方法。

EOF

其實(shí)偏向鎖還有一個(gè)撤銷(xiāo)的過(guò)程,也是有代價(jià)的,但相比于偏向鎖帶好的好處,是能夠接受的。但我們這里重點(diǎn)的還是關(guān)注鎖升級(jí)的具體邏輯和細(xì)節(jié),關(guān)于鎖升級(jí)的過(guò)程就聊到這里。

到此這篇關(guān)于Java Synchronized鎖升級(jí)過(guò)程的文章就介紹到這了,更多相關(guān)Java Synchronized鎖升級(jí)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java序列化框架Kryo高效轉(zhuǎn)換對(duì)象為字節(jié)流面試精講

    Java序列化框架Kryo高效轉(zhuǎn)換對(duì)象為字節(jié)流面試精講

    這篇文章主要為大家介紹了Java序列化框架Kryo高效轉(zhuǎn)換對(duì)象為字節(jié)流面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • 5種必會(huì)的Java異步調(diào)用轉(zhuǎn)同步的方法你會(huì)幾種

    5種必會(huì)的Java異步調(diào)用轉(zhuǎn)同步的方法你會(huì)幾種

    這篇文章主要介紹了5種必會(huì)的Java異步調(diào)用轉(zhuǎn)同步的方法你會(huì)幾種,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • java實(shí)現(xiàn)訂餐系統(tǒng)

    java實(shí)現(xiàn)訂餐系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)訂餐系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • idea中g(shù)it如何刪除commit提交的log信息

    idea中g(shù)it如何刪除commit提交的log信息

    這篇文章主要介紹了idea中g(shù)it如何刪除commit提交的log信息問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Java利用VLC開(kāi)發(fā)簡(jiǎn)易視屏播放器功能

    Java利用VLC開(kāi)發(fā)簡(jiǎn)易視屏播放器功能

    這篇文章主要介紹了Java利用VLC開(kāi)發(fā)簡(jiǎn)易視屏播放器,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • Java中sleep()與wait()的區(qū)別總結(jié)

    Java中sleep()與wait()的區(qū)別總結(jié)

    因?yàn)樽罱鼘W(xué)習(xí)時(shí)正好碰到這兩個(gè)方法,就查閱相關(guān)資料,并通過(guò)程序?qū)崿F(xiàn),進(jìn)行區(qū)別總結(jié)一下,所以下面這篇文章主要給大家總結(jié)介紹了關(guān)于Java中sleep()與wait()區(qū)別的相關(guān)資料,需要的朋友可以參考,下面來(lái)一起看看吧。
    2017-05-05
  • SpringBoot2.x 參數(shù)校驗(yàn)問(wèn)題小結(jié)

    SpringBoot2.x 參數(shù)校驗(yàn)問(wèn)題小結(jié)

    這篇文章主要介紹了SpringBoot2.x 參數(shù)校驗(yàn)一些問(wèn)題總結(jié),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • Java實(shí)現(xiàn)分布式系統(tǒng)限流

    Java實(shí)現(xiàn)分布式系統(tǒng)限流

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)分布式系統(tǒng)限流,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • IDEA 錯(cuò)誤之找不到或無(wú)法加載主類(lèi)的問(wèn)題

    IDEA 錯(cuò)誤之找不到或無(wú)法加載主類(lèi)的問(wèn)題

    這篇文章主要介紹了IDEA 錯(cuò)誤之找不到或無(wú)法加載主類(lèi),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Spring聲明式事務(wù)@Transactional知識(shí)點(diǎn)分享

    Spring聲明式事務(wù)@Transactional知識(shí)點(diǎn)分享

    在本篇文章里小編給大家整理了關(guān)于Spring聲明式事務(wù)@Transactional詳解內(nèi)容,需要的朋友們可以參考下。
    2020-02-02

最新評(píng)論