亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java synchronized的鎖升級(jí)過(guò)程詳解

 更新時(shí)間:2024年04月26日 09:05:27   作者:大明哥_  
在 JDK 1.6之前,synchronized 是一個(gè)重量級(jí)、效率比較低下的鎖,但是在JDK 1.6后,JVM 為了提高鎖的獲取與釋放效,,對(duì) synchronized 進(jìn)行了優(yōu)化,所以本文給大家介紹了synchronized的鎖升級(jí)過(guò)程,需要的朋友可以參考下

介紹

在 JDK 1.6之前,synchronized 是一個(gè)重量級(jí)、效率比較低下的鎖,但是在JDK 1.6后,JVM 為了提高鎖的獲取與釋放效,,對(duì) synchronized 進(jìn)行了優(yōu)化,引入了偏向鎖輕量級(jí)鎖,至此,鎖的狀態(tài)有四種,級(jí)別由低到高依次為:無(wú)鎖偏向鎖、輕量級(jí)鎖、重量級(jí)鎖

鎖升級(jí)就是無(wú)鎖 —> 偏向鎖 —> 輕量級(jí)鎖 —> 重量級(jí)鎖 的一個(gè)過(guò)程,注意,鎖只能升級(jí),不能降級(jí)。

原理詳解

對(duì)象頭

HotSpot 虛擬機(jī)中,對(duì)象在內(nèi)存中存儲(chǔ)布局可以分為三塊區(qū)域:對(duì)象頭(Header)、實(shí)例數(shù)據(jù)(Instance Data)和對(duì)齊填充(Padding):

  • 對(duì)象頭:分為Mark Word 和 對(duì)象指針

    • Mark Word:存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù),如哈希碼(HashCode)、GC分代年齡、鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向線程ID、偏向時(shí)間戳等。
    • 對(duì)象指針:存儲(chǔ)指向類(lèi)元數(shù)據(jù)的指針,使得能夠訪問(wèn)對(duì)象屬于的類(lèi)的信息。
  • 實(shí)例數(shù)據(jù):存儲(chǔ)對(duì)象的實(shí)際有效信息,也就是我們?cè)陬?lèi)中所定義的各種類(lèi)型的字段內(nèi)容。

  • 對(duì)齊填充:可選字段,通常存在于對(duì)象的末尾,用于確保對(duì)象的大小是8字節(jié)的倍數(shù)(因?yàn)樵S多JVM都使用8字節(jié)的對(duì)象對(duì)齊)。這是出于性能考慮,使得對(duì)象的地址在內(nèi)存中是對(duì)齊的。

synchronized 鎖相關(guān)的信息主要是在 Mark Word 區(qū)域,我們先看看 Mark Word。

Mark Word

synchronized 用的鎖存在鎖對(duì)象的對(duì)象頭的Mark Word中,我們先看 Mark Word 到底長(zhǎng)什么樣。

鎖分類(lèi)

無(wú)鎖

無(wú)鎖可以理解為單線程輕松愉快地運(yùn)行,沒(méi)有其他的線程來(lái)和其競(jìng)爭(zhēng)。但是無(wú)鎖不代表沒(méi)有同步,它只是表示鎖對(duì)象目前沒(méi)有被任何線程顯式鎖定。

偏向鎖

偏向鎖 JDK 1.6 引入的一種鎖優(yōu)化機(jī)制。

何謂“偏向”?就是鎖對(duì)象會(huì)偏向于第一個(gè)獲得它的線程。什么意思呢。

當(dāng)一個(gè)線程訪問(wèn)同步代碼塊并獲取鎖時(shí),該鎖會(huì)進(jìn)入偏向模式,鎖標(biāo)志的狀態(tài)將被設(shè)置為偏向(01),并且鎖的擁有者被設(shè)置為當(dāng)前線程(偏向鎖線程 id = 當(dāng)前線程 id)。當(dāng)該線程執(zhí)行完同步代碼塊后,線程并不會(huì)主動(dòng)釋放偏向鎖。當(dāng)線程再次進(jìn)入同步代碼塊時(shí),會(huì)首先判斷此時(shí)持有鎖的線程與它是否為同一線程,如果是則正常往下執(zhí)行,由于此前是沒(méi)有釋放鎖的,所以這次就不會(huì)有任何的獲取鎖操作。

所以,偏向鎖是指當(dāng)一段同步代碼一直被同一個(gè)線程所訪問(wèn)時(shí),就不存在所謂的多線程競(jìng)爭(zhēng)了,那么該線程在后續(xù)訪問(wèn)時(shí)便會(huì)自動(dòng)獲得鎖,從而降低獲取鎖帶來(lái)的消耗,即提高性能。

偏向鎖的鎖釋放是一個(gè)被動(dòng)過(guò)程,線程不會(huì)主動(dòng)釋放偏向鎖,只有當(dāng)其他線程來(lái)競(jìng)爭(zhēng)偏向鎖時(shí),JVM 才會(huì)檢測(cè)到鎖的狀態(tài)并觸發(fā)撤銷(xiāo)。但是撤銷(xiāo)需要等待全局安全點(diǎn)(所有線程會(huì)暫停),JVM 會(huì)在全局安全點(diǎn)時(shí)判斷鎖對(duì)象是否處于被鎖定狀態(tài),如果沒(méi)有被鎖定,且持有鎖的線程不處于活動(dòng)狀態(tài),則將對(duì)象頭設(shè)置為無(wú)鎖狀態(tài),并撤銷(xiāo)偏向鎖。

所以,引入偏向鎖的目的是認(rèn)為當(dāng)前環(huán)境下是不存在多線程競(jìng)爭(zhēng)的場(chǎng)景,可以認(rèn)為是單線程環(huán)境,同一個(gè)線程多次持有鎖,減少單線程環(huán)境下獲取鎖帶來(lái)的不必要。

流程圖如下:

輕量級(jí)鎖

當(dāng)一個(gè)線程持有偏向鎖時(shí),另外一個(gè)線程來(lái)競(jìng)爭(zhēng)鎖,這時(shí)偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖。

輕量級(jí)鎖的競(jìng)爭(zhēng)方式一種比較輕量級(jí)的競(jìng)爭(zhēng)方式,當(dāng)某個(gè)線程沒(méi)有獲取到鎖,它并不是立刻被掛起,而是采取自旋的方式來(lái)競(jìng)爭(zhēng)鎖資源。在競(jìng)爭(zhēng)較少的情況下,輕量級(jí)鎖通過(guò)減少線程阻塞和喚醒操作,可以提高性能。

輕量級(jí)鎖的目的在于它認(rèn)為系統(tǒng)當(dāng)前的競(jìng)爭(zhēng)環(huán)境不是激烈,如果采取阻塞和喚醒線程的方式,則會(huì)過(guò)多地消耗系統(tǒng)資源。如果某個(gè)線程沒(méi)有獲取到輕量級(jí)鎖,則采取自旋的方式來(lái)判斷鎖資源是否已被釋放。這種方式減少了上線文的切換。

但是長(zhǎng)時(shí)間的自旋操作是非常消耗資源的,一個(gè)線程獲取了輕量級(jí)鎖,其他線程就只能在那里“空耗”,它們不釋放 CPU 資源,但也不做任何事,這種現(xiàn)象叫做忙等busy-waiting)。所以,我們是允許短時(shí)間的忙等,用它來(lái)?yè)Q取線程在用戶態(tài)和內(nèi)核態(tài)之間切換的開(kāi)銷(xiāo)。

觸發(fā)輕量級(jí)鎖的條件是兩個(gè):

  • 關(guān)閉偏向鎖(-XX:-UseBiasedLocking
  • 多個(gè)線程競(jìng)爭(zhēng)偏向鎖導(dǎo)致偏向鎖升級(jí)為輕量級(jí)鎖

流程圖如下:

重量級(jí)鎖

輕量級(jí)鎖自旋是要有限度的,你不能一直在那里空轉(zhuǎn),所以如果鎖競(jìng)爭(zhēng)環(huán)境比較嚴(yán)重,當(dāng)自旋次數(shù)達(dá)到某個(gè)閾值(默認(rèn) 10 次,可自動(dòng)調(diào)整)后,就是停止自旋,此時(shí)鎖膨脹為重量級(jí)鎖。當(dāng)其膨脹為重量級(jí)鎖后,其他線程就不再是等待了,而是阻塞等待。重量級(jí)鎖依賴對(duì)象內(nèi)部的監(jiān)視器(monitor)實(shí)現(xiàn),而 monitor 依賴的是操作系統(tǒng)的 MutexLock(互斥鎖)。

由于是重量級(jí)鎖,那么等待鎖資源的線程都會(huì)被阻塞,雖然阻塞的線程不會(huì)消耗 CPU,但是阻塞或者喚醒一個(gè)線程都需要通過(guò)底層操作系統(tǒng)來(lái)實(shí)現(xiàn),它會(huì)涉及到上下文切換,用戶態(tài)和內(nèi)核態(tài)之間的轉(zhuǎn)換,這本身就是一個(gè)非常重量級(jí)、高開(kāi)銷(xiāo)的操作。

鎖升級(jí)過(guò)程

鎖升級(jí)就是無(wú)鎖 —> 偏向鎖 —> 輕量級(jí)鎖 —> 重量級(jí)鎖 的一個(gè)過(guò)程,注意,鎖只能升級(jí),不能降級(jí)。流程圖如下:

  • JVM 啟動(dòng)后,鎖資源對(duì)象直到有第一個(gè)線程訪問(wèn)時(shí),它都是無(wú)鎖狀態(tài),此時(shí) Mark Word 內(nèi)容如下:

偏向鎖標(biāo)識(shí)為 0,鎖標(biāo)識(shí)為 01。

  • 當(dāng)鎖對(duì)象首次被某個(gè)線程(假如為線程 A,id 為 1000001)時(shí),鎖就會(huì)從無(wú)鎖狀態(tài)升級(jí)偏向鎖。偏向鎖會(huì)在 Mark Word 中的偏向鎖線程 id 存儲(chǔ)當(dāng)前線程的id(1000001),偏向鎖標(biāo)識(shí)為 1,鎖標(biāo)識(shí)為 01,如下:

如果當(dāng)前線程再次獲取該鎖對(duì)象,只需要比較偏向鎖線程 id 即可。

  • 當(dāng)有其他線程(假如為線程 B,id 為 1000002)來(lái)競(jìng)爭(zhēng)該鎖對(duì)象,此時(shí)鎖為偏向鎖,這個(gè)時(shí)候會(huì)比較偏向鎖的線程 id 是否為線程 B 1000002,我們可以判斷不是,所以會(huì)利用 CAS 嘗試修改 Mark Word,如果成功,則線程 B 獲取偏向鎖成功,此時(shí) Mark Word 中的偏向鎖線程 id 為線程 B id 1000002

  • 但如果失敗了,就說(shuō)明當(dāng)前環(huán)境可能存在鎖競(jìng)爭(zhēng),則需要執(zhí)行偏向鎖撤銷(xiāo)操作。等到全局安全點(diǎn)時(shí),JVM 會(huì)暫停持有偏向鎖的線程 A,檢查線程 A 的狀態(tài),若線程 A狀態(tài)為不活躍或者已經(jīng)執(zhí)行完了同步代碼塊,則設(shè)置鎖對(duì)象為無(wú)鎖狀態(tài)(線程 ID 為空,偏向鎖 0 ,鎖標(biāo)志位為01)重新偏向,同時(shí)恢復(fù)線程 A,繼續(xù)獲取偏向鎖。如果線程 A 的同步代碼塊還沒(méi)執(zhí)行完,則需要升級(jí)為輕量級(jí)鎖。
  • 在升級(jí)為輕量級(jí)鎖之前,持有偏向鎖的線程 A是暫停的,JVM 首先會(huì)在線程 A 的棧中創(chuàng)建一個(gè)名為鎖記錄的空間(Lock Record),用于存放鎖對(duì)象目前的 Mark Word 的拷貝,然后拷貝對(duì)象頭中的 Mark Word 到線程 A 的鎖記錄中(官方稱之為 Displaced Mark Word ),若拷貝成功,JVM 將使用 CAS 嘗試將對(duì)象頭重的 Mark Word 更新為指向線程 A 的 Lock Record 的指針,成功,線程 A 獲取輕量級(jí)鎖,此時(shí) Mark Word 的鎖標(biāo)志位為 00,指向鎖記錄的指針指向線程 A 的鎖記錄地址,如下圖:

  • 對(duì)于其他線程而言,也會(huì)在棧幀中建立鎖記錄,存儲(chǔ)鎖對(duì)象目前的 Mark Word 的拷貝。也利用 CAS 嘗試將鎖對(duì)象的 Mark Word 更正指向自身線程的 Lock Record,如果成功,表明競(jìng)爭(zhēng)到輕量級(jí)鎖,則執(zhí)行同步代碼塊。如果失敗,那么線程嘗試使用自旋的方式來(lái)等待持有輕量級(jí)鎖的線程釋放鎖。當(dāng)然,它不會(huì)一直自旋下去,因?yàn)樽孕倪^(guò)程也會(huì)消耗 CPU,而是自旋一定的次數(shù),如果自旋了一定次數(shù)后還是失敗,則升級(jí)為重量級(jí)鎖,阻塞所有未獲取鎖的線程,等待釋放鎖后喚醒。

最后是,鎖升級(jí)過(guò)程的詳細(xì)流程(此圖來(lái)源于網(wǎng)上):

以上就是Java synchronized的鎖升級(jí)過(guò)程詳解的詳細(xì)內(nèi)容,更多關(guān)于Java synchronized鎖升級(jí)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java高級(jí)排序之希爾排序

    java高級(jí)排序之希爾排序

    這篇文章主要介紹了java高級(jí)排序之希爾排序 ,需要的朋友可以參考下
    2015-04-04
  • Java環(huán)境配置圖文教程(推薦)

    Java環(huán)境配置圖文教程(推薦)

    下面小編就為大家?guī)?lái)一篇Java環(huán)境配置圖文教程(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • Java并發(fā)之條件阻塞Condition的應(yīng)用代碼示例

    Java并發(fā)之條件阻塞Condition的應(yīng)用代碼示例

    這篇文章主要介紹了Java并發(fā)之條件阻塞Condition的應(yīng)用代碼示例,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • Java設(shè)計(jì)模式之抽象工廠模式AbstractFactoryPattern詳解

    Java設(shè)計(jì)模式之抽象工廠模式AbstractFactoryPattern詳解

    這篇文章主要介紹了Java設(shè)計(jì)模式之抽象工廠模式AbstractFactoryPattern詳解,抽象工廠模式是一種軟件開(kāi)發(fā)設(shè)計(jì)模式,抽象工廠模式提供了一種方式,可以將一組具有同一主題的單獨(dú)的工廠封裝起來(lái),需要的朋友可以參考下
    2023-10-10
  • Java Scanner對(duì)象中hasNext()與next()方法的使用

    Java Scanner對(duì)象中hasNext()與next()方法的使用

    這篇文章主要介紹了Java Scanner對(duì)象中hasNext()與next()方法的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java_int、double型數(shù)組常用操作工具類(lèi)(分享)

    Java_int、double型數(shù)組常用操作工具類(lèi)(分享)

    下面小編就為大家?guī)?lái)一篇Java_int、double型數(shù)組常用操作工具類(lèi)(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • java 正則,object中兩個(gè)方法的使用(詳解)

    java 正則,object中兩個(gè)方法的使用(詳解)

    下面小編就為大家?guī)?lái)一篇java 正則,object中兩個(gè)方法的使用(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • JAVA 時(shí)間區(qū)間的字符串合法性驗(yàn)證

    JAVA 時(shí)間區(qū)間的字符串合法性驗(yàn)證

    需要對(duì)獲得的諸如08:30-11:00這樣的字符串進(jìn)行合法性驗(yàn)證,判定表示的時(shí)間區(qū)間是否合法,以及對(duì)高峰期時(shí)間的區(qū)間是否在總的時(shí)間區(qū)間內(nèi)部進(jìn)行判斷。
    2013-03-03
  • 一文徹底弄懂零拷貝原理以及java實(shí)現(xiàn)

    一文徹底弄懂零拷貝原理以及java實(shí)現(xiàn)

    零拷貝(英語(yǔ): Zero-copy) 技術(shù)是指計(jì)算機(jī)執(zhí)行操作時(shí),CPU不需要先將數(shù)據(jù)從某處內(nèi)存復(fù)制到另一個(gè)特定區(qū)域,下面這篇文章主要給大家介紹了關(guān)于零拷貝原理以及java實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • Spring攔截器和過(guò)濾器的區(qū)別在哪?

    Spring攔截器和過(guò)濾器的區(qū)別在哪?

    相信很多小伙伴都對(duì)Spring攔截器和過(guò)濾器的區(qū)別有疑惑,今天特地整理了本篇文章,文中有非常詳細(xì)的介紹,需要的朋友可以參考下
    2021-06-06

最新評(píng)論