最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入剖析Java中的synchronized關(guān)鍵字

 更新時(shí)間:2023年06月18日 14:26:16   作者:半畝方塘立身  
在 Java 程序中,我們可以利用 synchronized 關(guān)鍵字來(lái)對(duì)程序進(jìn)行加鎖,它既可以用來(lái)聲明一個(gè) synchronized 代碼塊,也可以直接標(biāo)記靜態(tài)方法或者實(shí)例方法,本文就帶大家深入了解Java中的synchronized關(guān)鍵字,感興趣的同學(xué)可以參考閱讀

synchronized介紹

synchronized關(guān)鍵字可以解決的是多個(gè)線程之間訪問資源的同步性。synchronized關(guān)鍵字可以保證被它修飾的方法或者代碼塊在任意時(shí)刻只能有一個(gè)線程執(zhí)行。

在 Java 程序中,我們可以利用 synchronized 關(guān)鍵字來(lái)對(duì)程序進(jìn)行加鎖。它既可以用來(lái)聲明一個(gè) synchronized 代碼塊,也可以直接標(biāo)記靜態(tài)方法或者實(shí)例方法。

關(guān)鍵字在代碼塊上

代碼如下:

public void methodA() {
    Object obj = new Object();
    synchronized (obj) {
        //
    }
}

編譯結(jié)果(javap -v)

public void methodA();
descriptor: ()V
flags: ACC_PUBLIC
Code:
  stack=2, locals=4, args_size=1
     0: new           #3                  // class java/lang/Object
     3: dup
     4: invokespecial #1                  // Method java/lang/Object."<init>":()V
     7: astore_1
     8: aload_1
     9: dup
    10: astore_2
    11: monitorenter
    12: aload_2
    13: monitorexit
    14: goto          22
    17: astore_3
    18: aload_2
    19: monitorexit
    20: aload_3
    21: athrow
    22: return
  Exception table:
     from    to  target type
        12    14    17   any
        17    20    17   any

上面的字節(jié)碼中包含一個(gè) monitorenter 指令以及多個(gè) monitorexit 指令。這是因?yàn)?Java 虛擬機(jī)需要確保所獲得的鎖在正常執(zhí)行路徑,以及異常執(zhí)行路徑上都能夠被解鎖。 synchronized 應(yīng)用在同步塊上時(shí),在字節(jié)碼中是通過(guò) monitorenter 和 monitorexit 實(shí)現(xiàn)的。

關(guān)鍵字在方法上

代碼如下:

public synchronized void methodB() {  
    //  
    i++;  
}

當(dāng)synchronized修飾同步方法時(shí),編譯器會(huì)在生成的字節(jié)碼中添加一個(gè)額外的指令來(lái)獲取和釋放方法的監(jiān)視器鎖(monitor lock)。同時(shí),編譯器還會(huì)設(shè)置方法的ACC_SYNCHRONIZED標(biāo)志。

public synchronized void methodB();
  descriptor: ()V
  flags: ACC\_PUBLIC, ACC\_SYNCHRONIZED
  Code:
    stack=3, locals=1, args\_size=1
        0: aload\_0
        1: dup
        2: getfield      #2                  // Field i:I
        5: iconst\_1
        6: iadd
        7: putfield      #2                  // Field i:I
        10: return
        LineNumberTable:
        line 15: 0
        line 16: 10

當(dāng)JVM加載字節(jié)碼文件并解析類的時(shí)候,會(huì)檢查方法的訪問標(biāo)志。如果ACC_SYNCHRONIZED標(biāo)志被設(shè)置,表示在進(jìn)入該方法時(shí),Java 虛擬機(jī)需要進(jìn)行 monitorenter 操作。而在退出該方法時(shí),不管是正常返回,還是向調(diào)用者拋異常,Java 虛擬機(jī)均需要進(jìn)行 monitorexit 操作。

這里 monitorenter 和 monitorexit 操作所對(duì)應(yīng)的鎖對(duì)象是隱式的。對(duì)于實(shí)例方法來(lái)說(shuō),這兩個(gè)操作對(duì)應(yīng)的鎖對(duì)象是 this;對(duì)于靜態(tài)方法來(lái)說(shuō),這兩個(gè)操作對(duì)應(yīng)的鎖對(duì)象則是所在類的 Class 實(shí)例。

synchronized原理

synchronized 對(duì)對(duì)象進(jìn)行加鎖,在 JVM 中,對(duì)象在內(nèi)存中分為三塊區(qū)域:對(duì)象頭、實(shí)例數(shù)據(jù)和對(duì)齊填充。在對(duì)象頭中保存了鎖標(biāo)志位和指向 monitor 對(duì)象的起始地址,如下圖所示,右側(cè)就是對(duì)象對(duì)應(yīng)的 Monitor 對(duì)象。當(dāng) Monitor 被某個(gè)線程持有后,就會(huì)處于鎖定狀態(tài),如圖中的 Owner 部分,會(huì)指向持有 Monitor 對(duì)象的線程。另外 Monitor 中還有兩個(gè)隊(duì)列,用來(lái)存放進(jìn)入及等待獲取鎖的線程。

為了提升性能,JDK1.6 引入了偏向鎖、輕量級(jí)鎖、重量級(jí)鎖概念,來(lái)減少鎖競(jìng)爭(zhēng)帶來(lái)的上下文切換,而正是新增的 Java 對(duì)象頭實(shí)現(xiàn)了鎖升級(jí)功能。當(dāng) Java 對(duì)象被 Synchronized 關(guān)鍵字修飾成為同步鎖后,圍繞這個(gè)鎖的一系列升級(jí)操作都將和 Java 對(duì)象頭有關(guān)。

對(duì)象頭

Java中對(duì)象頭由三個(gè)部分組成:Mark Word、Klass Pointer、Length。

  • Mark Word
    Mark WordMark Word記錄了與對(duì)象和鎖相關(guān)的信息,當(dāng)這個(gè)對(duì)象作為鎖對(duì)象來(lái)實(shí)現(xiàn)synchronized的同步操作時(shí),鎖標(biāo)記和相關(guān)信息都是存儲(chǔ)在Mark Word中的。

    64位系統(tǒng)Mark Word存儲(chǔ)結(jié)構(gòu)如下:

  • 從圖中可以看到一個(gè)鎖狀態(tài)的字段,它包含五種狀態(tài)分別是無(wú)鎖、偏向鎖、輕量級(jí)鎖、重量級(jí)鎖、GC標(biāo)記。通過(guò)1bit來(lái)表達(dá)無(wú)鎖和偏向鎖,其中0表示無(wú)鎖、1表示偏向鎖。

  • Klass PointerKlass Pointer
    Klass PointerKlass Pointer表示指向類的指針,JVM通過(guò)這個(gè)指針來(lái)確定對(duì)象具體屬于哪個(gè)類的實(shí)例。它的存儲(chǔ)長(zhǎng)度根據(jù)JVM的位數(shù)來(lái)決定,在32位的虛擬機(jī)中占4字節(jié),在64位的虛擬機(jī)中占8字節(jié),但是在JDK 1.8中,由于默認(rèn)開啟了指針壓縮,所以壓縮后在64位系統(tǒng)中只占4字節(jié)。

  • Length
    Length表示數(shù)組長(zhǎng)度,只有構(gòu)建對(duì)象數(shù)組時(shí)才會(huì)有數(shù)組長(zhǎng)度屬性。

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

當(dāng)一個(gè)線程訪問增加了synchronized關(guān)鍵字的代碼塊時(shí),如果偏向鎖是開啟狀態(tài),則先嘗試通過(guò)偏向鎖來(lái)獲得鎖資源,這個(gè)過(guò)程僅僅通過(guò)CAS來(lái)完成。如果當(dāng)前已經(jīng)有其他線程獲得了偏向鎖,那么搶占鎖資源的線程由于無(wú)法獲得鎖,所以會(huì)嘗試升級(jí)到輕量級(jí)鎖來(lái)進(jìn)行鎖資源搶占,輕量級(jí)鎖就是通過(guò)多次CAS(也就是自旋鎖)來(lái)完成的。如果這個(gè)線程通過(guò)多次自旋仍然無(wú)法獲得鎖資源,那么最終只能升級(jí)到重量級(jí)鎖來(lái)實(shí)現(xiàn)線程的等待。

下圖顯示了對(duì)象頭的布局和不同對(duì)象狀態(tài)的表示:

偏向鎖的原理

偏向鎖其實(shí)可以認(rèn)為是在沒有多線程競(jìng)爭(zhēng)的情況下訪問synchronized修飾的代碼塊的加鎖場(chǎng)景,也就是在單線程執(zhí)行的情況下。

實(shí)際上對(duì)程序開發(fā)來(lái)說(shuō),加鎖是為了防范線程安全性的風(fēng)險(xiǎn),但是是否有線程競(jìng)爭(zhēng)并不由我們來(lái)控制,而是由應(yīng)用場(chǎng)景來(lái)決定。假設(shè)這種情況存在,就沒有必要使用重量級(jí)鎖基于操作系統(tǒng)級(jí)別的Mutex Lock來(lái)實(shí)現(xiàn)鎖的搶占,這樣顯然很耗費(fèi)性能。

所以偏向鎖的作用就是,線程在沒有線程競(jìng)爭(zhēng)的情況下去訪問synchronized同步代碼塊時(shí),會(huì)嘗試先通過(guò)偏向鎖來(lái)?yè)屨荚L問資格,這個(gè)搶占過(guò)程是基于CAS來(lái)完成的,如果搶占鎖成功,則直接修改對(duì)象頭中的鎖標(biāo)記。其中,偏向鎖標(biāo)記為1,鎖標(biāo)記為01,以及存儲(chǔ)當(dāng)前獲得鎖的線程ID。而偏向的意思就是,如果線程X獲得了偏向鎖,那么當(dāng)線程X后續(xù)再訪問這個(gè)同步方法時(shí),只需要判斷對(duì)象頭中的線程ID和線程X是否相等即

獲取偏向鎖的流程

下圖代表獲取偏向鎖的粗粒度流程圖,偏向鎖是在沒有線程競(jìng)爭(zhēng)的情況下實(shí)現(xiàn)的一種鎖,不能排除存在鎖競(jìng)爭(zhēng)的情況,所以偏向鎖的獲取有兩種情況。

  • 沒有鎖競(jìng)爭(zhēng)

    在沒有鎖競(jìng)爭(zhēng)并且開啟了偏向鎖的情況下,當(dāng)線程1訪問synchronized(lock)修飾的代碼塊時(shí):

    • 從當(dāng)前線程的棧中找到一個(gè)空閑的BasicObjectLock(圖中Lock Record),它是一個(gè)基礎(chǔ)的鎖對(duì)象,在后續(xù)的輕量級(jí)鎖和重量級(jí)鎖中都會(huì)用到,BasicObjectLock包含以下兩個(gè)屬性。
      • BasicLock,該屬性中有一個(gè)字段markOop,用于保存指向lock鎖對(duì)象的對(duì)象頭數(shù)據(jù)。
      • oop,指向lock鎖對(duì)象的指針。
    • 將BasicObjectLock中的oop指針指向當(dāng)前的鎖對(duì)象lock。
    • 獲得當(dāng)前鎖對(duì)象lock的對(duì)象頭,通過(guò)對(duì)象頭來(lái)判斷是否可偏向,也就是說(shuō)鎖標(biāo)記為101,并且Thread Id為空。
      • 如果為可偏向狀態(tài),那么判斷當(dāng)前偏向的線程是不是線程1,如果偏向的是自己,則不需要再搶占鎖,直接有資格運(yùn)行同步代碼塊。
      • 如果為不可偏向狀態(tài),則需要通過(guò)輕量級(jí)鎖來(lái)完成鎖的搶占過(guò)程。
    • 如果對(duì)象鎖lock偏向其他線程或者當(dāng)前是匿名偏向狀態(tài)(也就是沒有偏向任何一個(gè)線程),則先構(gòu)建一個(gè)匿名偏向的Mark Word,然后通過(guò)CAS方法,把一個(gè)匿名偏向的Mark Word修改為偏向線程1。如果當(dāng)前鎖對(duì)象lock已經(jīng)偏向了其他線程,那么CAS一定會(huì)失敗。
  • 存在鎖競(jìng)爭(zhēng)

    假設(shè)線程1獲得了偏向鎖,此時(shí)線程2去執(zhí)行synchronized(lock)同步代碼塊,如果訪問到同一個(gè)對(duì)象鎖則會(huì)觸發(fā)鎖競(jìng)爭(zhēng)并觸發(fā)偏向鎖撤銷,撤銷流程如下。

    • 線程2調(diào)用撤銷偏向鎖方法,嘗試撤銷lock鎖對(duì)象的偏向鎖。
    • 撤銷偏向鎖需要到達(dá)全局安全點(diǎn)(SafePoint)才會(huì)執(zhí)行,全局安全點(diǎn)就是當(dāng)前線程運(yùn)行到的這個(gè)位置,線程的狀態(tài)可以被確定,堆對(duì)象的狀態(tài)也是確定的,在這個(gè)位置JVM可以安全地進(jìn)行GC、偏向鎖撤銷等動(dòng)作。當(dāng)?shù)竭_(dá)全局安全點(diǎn)后,會(huì)暫停獲得偏向鎖的線程1。
    • 檢查獲得偏向鎖的線程1的狀態(tài),這里存在兩種狀態(tài)。
      • 線程1已經(jīng)執(zhí)行完同步代碼塊或者處于非存活狀態(tài)。在這種情況下,直接把偏向鎖撤銷恢復(fù)成無(wú)鎖狀態(tài),然后線程2升級(jí)到輕量級(jí)鎖,通過(guò)輕量級(jí)鎖搶占鎖資源。
      • 線程1還在執(zhí)行同步代碼塊中的指令,也就是說(shuō)沒有退出同步代碼塊。在這種情況下,直接把鎖對(duì)象lock升級(jí)成輕量級(jí)鎖(由于這里是全局安全點(diǎn),所以不需要通過(guò)CAS來(lái)實(shí)現(xiàn)),并且指向線程1,表示線程1持有輕量級(jí)鎖,接著線程1繼續(xù)執(zhí)行同步代碼塊中的代碼。

偏向鎖的釋放

在偏向鎖執(zhí)行完synchronized同步代碼塊后,會(huì)觸發(fā)偏向鎖釋放的流程,需要注意的是,偏向鎖本質(zhì)上并沒有釋放,因?yàn)楫?dāng)前鎖對(duì)象lock仍然是偏向該線程的。釋放的過(guò)程只是把Lock Record釋放了,也就是說(shuō)把Lock Record保存的鎖對(duì)象的Mark Word設(shè)置為空。

偏向鎖批量重偏向當(dāng)一個(gè)鎖對(duì)象lock只被同一個(gè)線程訪問時(shí),該鎖對(duì)象的鎖狀態(tài)就是偏向鎖,并且一直偏向該線程。當(dāng)有任何一個(gè)線程來(lái)訪問該鎖對(duì)象lock時(shí),不管之前獲得偏向鎖線程的狀態(tài)是存活還是死亡,lock鎖對(duì)象都會(huì)升級(jí)為輕量級(jí)鎖,并且鎖在升級(jí)之后是不可逆的。

假設(shè)一個(gè)線程t1針對(duì)大量的鎖對(duì)象增加了偏向鎖,之后線程t2來(lái)訪問這些鎖對(duì)象,在不考慮鎖競(jìng)爭(zhēng)的情況下,需要對(duì)之前所有偏向線程t1的鎖對(duì)象進(jìn)行偏向鎖撤銷和升級(jí),這個(gè)過(guò)程比較耗時(shí),而且虛擬機(jī)會(huì)認(rèn)為這個(gè)鎖不適合再偏向于原來(lái)的t1線程,于是當(dāng)偏向鎖撤銷次數(shù)達(dá)到20次時(shí),會(huì)觸發(fā)批量重偏向,把所有的鎖對(duì)象全部偏向線程t2。偏向鎖撤銷并批量重偏向的觸發(fā)閾值可以通過(guò)XX:BiasedLockingBulkRebiasThreshold = 20來(lái)配置,默認(rèn)是20。

在高并發(fā)場(chǎng)景下,當(dāng)大量線程同時(shí)競(jìng)爭(zhēng)同一個(gè)鎖資源時(shí),偏向鎖就會(huì)被撤銷,發(fā)生 stop the word 后, 開啟偏向鎖無(wú)疑會(huì)帶來(lái)更大的性能開銷,這時(shí)我們可以通過(guò)添加 JVM 參數(shù)關(guān)閉偏向鎖來(lái)調(diào)優(yōu)系統(tǒng)性能:

-XX:-UseBiasedLocking //關(guān)閉偏向鎖(默認(rèn)打開)
或者
-XX:+UseHeavyMonitors  //設(shè)置重量級(jí)鎖

輕量級(jí)鎖的原理

在線程沒有競(jìng)爭(zhēng)時(shí),使用偏向鎖能夠在不影響性能的前提下獲得鎖資源,但是同一時(shí)刻只允許一個(gè)線程獲得鎖資源,如果有多個(gè)線程來(lái)訪問同步方法,于是就有了輕量級(jí)鎖的設(shè)計(jì)。

所謂的輕量級(jí)鎖,就是沒有搶占到鎖的線程,進(jìn)行一定次數(shù)的重試(CAS)。比如線程第一次沒搶到鎖則重試幾次,如果在重試的過(guò)程中搶占到了鎖,那么這個(gè)線程就不需要阻塞,這種實(shí)現(xiàn)方式我們稱為自旋鎖,具體的實(shí)現(xiàn)流程如圖所示。

線程通過(guò)重試來(lái)?yè)屨兼i的方式是有代價(jià)的,因?yàn)榫€程如果不斷自旋重試,那么CPU會(huì)一直處于運(yùn)行狀態(tài)。如果持有鎖的線程占有鎖的時(shí)間比較短,那么自旋等待的實(shí)現(xiàn)帶來(lái)性能的提升會(huì)比較明顯。反之,如果持有鎖的線程占用鎖資源的時(shí)間比較長(zhǎng),那么自旋的線程就會(huì)浪費(fèi)CPU資源,所以線程重試搶占鎖的次數(shù)必須要有一個(gè)限制。從 JDK1.7 開始,自旋鎖默認(rèn)啟用,自旋次數(shù)由 JVM 設(shè)置決定,根據(jù)前一次在同一個(gè)鎖上的自旋次數(shù)及鎖持有者的狀態(tài)來(lái)決定的。如果在同一個(gè)鎖對(duì)象上,通過(guò)自旋等待成功獲得過(guò)鎖,并且持有鎖的線程正在運(yùn)行中,那么JVM會(huì)認(rèn)為此次自旋也有很大的機(jī)會(huì)獲得鎖,因此會(huì)將這個(gè)線程的自旋時(shí)間相對(duì)延長(zhǎng)。反之,如果在一個(gè)鎖對(duì)象中,通過(guò)自旋鎖獲得鎖很少成功,那么JVM會(huì)縮短自旋次數(shù)。

在高負(fù)載、高并發(fā)的場(chǎng)景下,我們可以通過(guò)設(shè)置 JVM 參數(shù)來(lái)關(guān)閉自旋鎖,優(yōu)化系統(tǒng)性能:

-XX:-UseSpinning //參數(shù)關(guān)閉自旋鎖優(yōu)化(默認(rèn)打開) 
-XX:PreBlockSpin //參數(shù)修改默認(rèn)的自旋次數(shù)。JDK1.7后,去掉此參數(shù),由jvm控制

如果偏向鎖存在競(jìng)爭(zhēng)或者偏向鎖未開啟,那么當(dāng)線程訪問synchronized(lock)同步代碼塊時(shí)就會(huì)采用輕量級(jí)鎖來(lái)?yè)屨兼i資源,獲得訪問資格,輕量級(jí)鎖的加鎖如圖所示:

獲取輕量級(jí)鎖的流程

  • 在線程2進(jìn)入同步代碼塊后,JVM會(huì)給當(dāng)前線程分配一個(gè)Lock Record,也就是一個(gè)BasicObjectLock對(duì)象,在它的成員對(duì)象BasicLock中有一個(gè)成員屬性markOop _displaced_header,這個(gè)屬性專門用來(lái)保存鎖對(duì)象lock的原始Mark Word。

  • 構(gòu)建一個(gè)無(wú)鎖狀態(tài)的Mark Word(其實(shí)就是lock鎖對(duì)象的Mark Word,但是鎖狀態(tài)是無(wú)鎖),把這個(gè)無(wú)鎖狀態(tài)的Mark Word設(shè)置到Lock Record中的_displaced_header字段中,如圖所示:

  • 通過(guò)CAS將lock鎖對(duì)象的Mark Word替換為指向Lock Record的指針,如果替換成功,就會(huì)得到如圖所示的結(jié)構(gòu),表示輕量級(jí)鎖搶占成功,此時(shí)線程2可以執(zhí)行同步代碼塊。

  • 如果CAS失敗,則說(shuō)明當(dāng)前l(fā)ock鎖對(duì)象不是無(wú)鎖狀態(tài),會(huì)觸發(fā)鎖膨脹,升級(jí)到重量級(jí)鎖。

相對(duì)偏向鎖來(lái)說(shuō),輕量級(jí)鎖的原理比較簡(jiǎn)單,它只是通過(guò)CAS來(lái)修改鎖對(duì)象中指向Lock Record的指針。從功能層面來(lái)說(shuō),偏向鎖和輕量級(jí)鎖最大的不同是:

  • 偏向鎖只能保證偏向同一個(gè)線程,只要有線程獲得過(guò)偏向鎖,那么當(dāng)其他線程去搶占鎖時(shí),只能通過(guò)輕量級(jí)鎖來(lái)實(shí)現(xiàn),除非觸發(fā)了重新偏向(如果獲得輕量級(jí)鎖的線程在后續(xù)的20次訪問中,發(fā)現(xiàn)每次訪問鎖的線程都是同一個(gè),則會(huì)觸發(fā)重新偏向,20次的定義屬性為:XX:BiasedLockingBulkRebiasThreshold =20)。
  • 輕量級(jí)鎖可以靈活釋放,也就是說(shuō),如果線程1搶占了輕量級(jí)鎖,那么在鎖用完并釋放后,線程2可以繼續(xù)通過(guò)輕量級(jí)鎖來(lái)?yè)屨兼i資源。

輕量級(jí)鎖的釋放

偏向鎖也有鎖釋放的邏輯,但是它只是釋放Lock Record,原本的偏向關(guān)系仍然存在,所以并不是真正意義上的鎖釋放。而輕量級(jí)鎖釋放之后,其他線程可以繼續(xù)使用輕量級(jí)鎖來(lái)?yè)屨兼i資源,具體的實(shí)現(xiàn)流程如下。

  • 把Lock Record中_displaced_header存儲(chǔ)的lock鎖對(duì)象的Mark Word替換到lock鎖對(duì)象的Mark Word中,這個(gè)過(guò)程會(huì)采用CAS來(lái)完成。
  • 如果CAS成功,則輕量級(jí)鎖釋放完成。
  • 如果CAS失敗,說(shuō)明釋放鎖的時(shí)候發(fā)生了競(jìng)爭(zhēng),就會(huì)觸發(fā)鎖膨脹,完成鎖膨脹之后,再調(diào)用重量級(jí)鎖的釋放鎖方法,完成鎖的釋放過(guò)程。

重量級(jí)鎖的原理分析

輕量級(jí)鎖能夠通過(guò)一定次數(shù)的重試讓沒有獲得鎖的線程有可能搶占到鎖資源,但是輕量級(jí)鎖只有在獲得鎖的線程持有鎖的時(shí)間較短的情況下才能起到提升同步鎖性能的效果。如果持有鎖的線程占用鎖資源的時(shí)間較長(zhǎng),那么不能讓那些沒有搶占到鎖資源的線程不斷自旋,否則會(huì)占用過(guò)多的CPU資源,這反而是一件得不償失的事情。如果沒搶占到鎖資源的線程通過(guò)一定次數(shù)的自旋后,發(fā)現(xiàn)仍然沒有獲得鎖,就只能阻塞等待了,所以最終會(huì)升級(jí)到重量級(jí)鎖,通過(guò)系統(tǒng)層面的互斥量(Mutex)來(lái)?yè)屨兼i資源。重量級(jí)鎖的實(shí)現(xiàn)原理如圖所示:

如果線程在運(yùn)行synchronized(lock)同步代碼塊時(shí),發(fā)現(xiàn)鎖狀態(tài)是輕量級(jí)鎖并且有其他線程搶占了鎖資源,那么該線程就會(huì)觸發(fā)鎖膨脹升級(jí)到重量級(jí)鎖。因此,重量級(jí)鎖是在存在線程競(jìng)爭(zhēng)的場(chǎng)景中使用的鎖類型。

獲取重量級(jí)鎖的流程

重量級(jí)鎖的實(shí)現(xiàn)流程如圖所示:

重量級(jí)鎖的實(shí)現(xiàn)是在ObjectMonitor中完成的,所以鎖膨脹的意義就是構(gòu)建一個(gè)ObjectMonitor,繼續(xù)關(guān)注圖中ObjectMonitor的實(shí)現(xiàn)部分,在ObjectMonitor中鎖的實(shí)現(xiàn)過(guò)程如下:

  • 首先,判斷當(dāng)前線程是否是重入,如果是則增加重入次數(shù)。
  • 然后,通過(guò)自旋鎖來(lái)實(shí)現(xiàn)鎖的搶占(這個(gè)自旋鎖就是前面我們提到的自適應(yīng)自旋),這里使用CAS機(jī)制來(lái)判斷ObjectMonitor中的_owner字段是否為空,如果為空就表示重量級(jí)鎖已釋放,當(dāng)前線程可以獲得鎖,否則就進(jìn)行自適應(yīng)自旋重試。
  • 最后,如果通過(guò)自旋鎖競(jìng)爭(zhēng)鎖失敗,則會(huì)把當(dāng)前線程構(gòu)建成一個(gè)ObjectWaiter節(jié)點(diǎn),插入_cxq隊(duì)列的隊(duì)首,再使用park方法阻塞當(dāng)前線程。

重量級(jí)鎖的釋放

鎖的釋放是在synchronized同步代碼塊結(jié)束后觸發(fā)的,釋放的邏輯比較簡(jiǎn)單。

  • 把ObjectMonitor中持有鎖的對(duì)象_owner置為null。
  • 從_cxq隊(duì)列中喚醒一個(gè)處于鎖阻塞的線程。
  • 被喚醒的線程會(huì)重新競(jìng)爭(zhēng)重量級(jí)鎖,需要注意的是,synchronized是非公平鎖,因此被喚醒后不一定能夠搶占到鎖,如果沒搶到,則繼續(xù)等待。

總結(jié)

JVM 在 JDK1.6 中引入了分級(jí)鎖機(jī)制來(lái)優(yōu)化 Synchronized,當(dāng)一個(gè)線程獲取鎖時(shí),首先對(duì)象鎖將成為一個(gè)偏向鎖,這樣做是為了優(yōu)化同一線程重復(fù)獲取導(dǎo)致的用戶態(tài)與內(nèi)核態(tài)的切換問題;其次如果有多個(gè)線程競(jìng)爭(zhēng)鎖資源,鎖將會(huì)升級(jí)為輕量級(jí)鎖,它適用于在短時(shí)間內(nèi)持有鎖,且分鎖有交替切換的場(chǎng)景;輕量級(jí)鎖還使用了自旋鎖來(lái)避免線程用戶態(tài)與內(nèi)核態(tài)的頻繁切換,大大地提高了系統(tǒng)性能;但如果鎖競(jìng)爭(zhēng)太激烈了,那么同步鎖將會(huì)升級(jí)為重量級(jí)鎖。減少鎖競(jìng)爭(zhēng),是優(yōu)化 Synchronized 同步鎖的關(guān)鍵。我們應(yīng)該盡量使 Synchronized 同步鎖處于輕量級(jí)鎖或偏向鎖,這樣才能提高 Synchronized 同步鎖的性能;通過(guò)減小鎖粒度來(lái)降低鎖競(jìng)爭(zhēng)也是一種最常用的優(yōu)化方法;另外我們還可以通過(guò)減少鎖的持有時(shí)間來(lái)提高 Synchronized 同步鎖在自旋時(shí)獲取鎖資源的成功率,避免 Synchronized 同步鎖升級(jí)為重量級(jí)鎖。

以上就是深入剖析Java中的synchronized關(guān)鍵字的詳細(xì)內(nèi)容,更多關(guān)于Java synchronized關(guān)鍵字的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringMVC如何域?qū)ο蠊蚕頂?shù)據(jù)

    SpringMVC如何域?qū)ο蠊蚕頂?shù)據(jù)

    在Spring MVC中,可以使用域?qū)ο髞?lái)共享數(shù)據(jù),域?qū)ο笫且粋€(gè)Map類型的對(duì)象,可以在請(qǐng)求處理方法之間共享數(shù)據(jù),本文給大家介紹SpringMVC 域?qū)ο蠊蚕頂?shù)據(jù)的示例代碼,一起看看吧
    2023-09-09
  • Spring AOP注解實(shí)戰(zhàn)指南

    Spring AOP注解實(shí)戰(zhàn)指南

    在現(xiàn)代軟件開發(fā)中,面向切面編程(AOP)是一種強(qiáng)大的編程范式,本文將介紹如何在Spring框架中通過(guò)AspectJ注解以及對(duì)應(yīng)的XML配置來(lái)實(shí)現(xiàn)AOP,在不改變主業(yè)務(wù)邏輯的情況下增強(qiáng)應(yīng)用程序的功能,需要的朋友可以參考下
    2024-06-06
  • java String的intern方法

    java String的intern方法

    本文主要介紹java 中String 的intern方法,這里主要通過(guò)實(shí)例來(lái)說(shuō)明不同版本的JDK,intern方法的對(duì)比,有需要的小伙伴可以參考下
    2016-07-07
  • java向mysql插入數(shù)據(jù)亂碼問題的解決方法

    java向mysql插入數(shù)據(jù)亂碼問題的解決方法

    這篇文章主要為大家詳細(xì)介紹了java向mysql插入數(shù)據(jù)亂碼問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • Spring Boot 啟動(dòng)加載數(shù)據(jù) CommandLineRunner的使用

    Spring Boot 啟動(dòng)加載數(shù)據(jù) CommandLineRunner的使用

    本篇文章主要介紹了Spring Boot 啟動(dòng)加載數(shù)據(jù) CommandLineRunner的使用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-04-04
  • SpringBoot配置開發(fā)環(huán)境的詳細(xì)步驟(JDK、Maven、IDEA等)

    SpringBoot配置開發(fā)環(huán)境的詳細(xì)步驟(JDK、Maven、IDEA等)

    文章介紹了如何配置SpringBoot開發(fā)環(huán)境,包括安裝JDK、Maven和IDEA,并提供了詳細(xì)的步驟和配置方法,感興趣的朋友一起看看吧
    2024-12-12
  • Java字符串的intern方法有何奧妙之處

    Java字符串的intern方法有何奧妙之處

    intern() 方法返回字符串對(duì)象的規(guī)范化表示形式。它遵循以下規(guī)則:對(duì)于任意兩個(gè)字符串 s 和 t,當(dāng)且僅當(dāng) s.equals(t) 為 true 時(shí),s.intern() == t.intern() 才為 true
    2021-10-10
  • java自定義注解驗(yàn)證手機(jī)格式的實(shí)現(xiàn)示例

    java自定義注解驗(yàn)證手機(jī)格式的實(shí)現(xiàn)示例

    這篇文章主要介紹了java自定義注解驗(yàn)證手機(jī)格式的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • 詳解SpringBoot和SpringBatch 使用

    詳解SpringBoot和SpringBatch 使用

    Spring Batch 是一個(gè)輕量級(jí)的、完善的批處理框架,旨在幫助企業(yè)建立健壯、高效的批處理應(yīng)用。這篇文章主要介紹了詳解SpringBoot和SpringBatch 使用,需要的朋友可以參考下
    2018-07-07
  • Spring?Boot獲取resources目錄下的文件三種方式詳解

    Spring?Boot獲取resources目錄下的文件三種方式詳解

    在Spring?Boot項(xiàng)目中,經(jīng)常需要獲取resources目錄下的文件,這些文件可以包括配置文件、模板文件、靜態(tài)資源等,這篇文章主要介紹了Spring?Boot獲取resources目錄下的文件的三種方式,需要的朋友可以參考下
    2023-06-06

最新評(píng)論

临海市| 沙雅县| 石台县| 神池县| 积石山| 前郭尔| 阿勒泰市| 溧阳市| 定陶县| 淮北市| 鸡西市| 金川县| 监利县| 平顺县| 甘孜县| 辽阳市| 东丽区| 荣昌县| 库伦旗| 龙口市| 襄垣县| 来宾市| 西藏| 郧西县| 乌兰县| 遂平县| 黄陵县| 鄂尔多斯市| 宜兰县| 黎川县| 鄯善县| 吴川市| 饶河县| 金川县| 凤庆县| 鄂伦春自治旗| 太白县| 辽阳市| 焦作市| 德格县| 德昌县|