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

Java?ynchronized重量級(jí)鎖的核心原理詳解

 更新時(shí)間:2022年03月01日 16:45:34   作者:小小茶花女  
這篇文章主要為大家詳細(xì)介紹了Java?ynchronized重量級(jí)鎖的核心原理,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助

在JVM中,每個(gè)對(duì)象都關(guān)聯(lián)一個(gè)監(jiān)視器,這里的對(duì)象包含Object實(shí)例和Class實(shí)例。監(jiān)視器是一個(gè)同步工具,相當(dāng)于一個(gè)許可證,拿到許可證的線程即可進(jìn)入臨界區(qū)進(jìn)行操作,沒(méi)有拿到則需要阻塞等待。重量級(jí)鎖通過(guò)監(jiān)視器的方式保障了任何時(shí)間只允許一個(gè)線程通過(guò)受到監(jiān)視器保護(hù)的臨界區(qū)代碼。

1. monitor原理

jvm中每個(gè)對(duì)象都會(huì)有一個(gè)監(jiān)視器Monitor,監(jiān)視器和對(duì)象一起創(chuàng)建、銷毀。監(jiān)視器相當(dāng)于一個(gè)用來(lái)監(jiān)視這些線程進(jìn)入的特殊房間,其義務(wù)是保證(同一時(shí)間)只有一個(gè)線程可以訪問(wèn)被保護(hù)的臨界區(qū)代碼塊。

每一個(gè)鎖都對(duì)應(yīng)一個(gè)monitor對(duì)象,在HotSpot虛擬機(jī)中它是由ObjectMonitor實(shí)現(xiàn)的(C++實(shí)現(xiàn))

//部分屬性
ObjectMonitor() {
    _count        = 0;     //鎖計(jì)數(shù)器
    _owner        = NULL;
    _WaitSet      = NULL;  //處于wait狀態(tài)的線程,會(huì)被加入到_WaitSet
    _EntryList    = NULL ; //處于等待鎖block狀態(tài)的線程,會(huì)被加入到該列表
  }

本質(zhì)上,監(jiān)視器是一種同步工具,也可以說(shuō)是一種同步機(jī)制,主要特點(diǎn)是:

  • 同步。監(jiān)視器所保護(hù)的臨界區(qū)代碼是互斥地執(zhí)行的。一個(gè)監(jiān)視器是一個(gè)運(yùn)行許可,任一線程進(jìn)入臨界區(qū)代碼都需要獲得這個(gè)許可,離開(kāi)時(shí)把許可歸還。
  • 協(xié)作。監(jiān)視器提供Signal機(jī)制,允許正持有許可的線程暫時(shí)放棄許可進(jìn)入阻塞等待狀態(tài),等待其他線程發(fā)送Signal去喚醒;其他擁有許可的線程可以發(fā)送Signal,喚醒正在阻塞等待的線程,讓它可以重新獲得許可并啟動(dòng)執(zhí)行。

在這里插入圖片描述

每個(gè)java 對(duì)象都可以關(guān)聯(lián)一個(gè) Monitor 對(duì)象,如果使用 synchronized 給對(duì)象上鎖(重量級(jí))之后,該對(duì)象頭的mark word中就被設(shè)置指向 Monitor 對(duì)象的指針。

(1) 如果使用 synchronized 給obj對(duì)象上鎖,obj對(duì)象的markword就會(huì)指向一個(gè)monitor鎖對(duì)象;

(2) 剛開(kāi)始 Monitor 中 Owner 為 null ;

(3) 當(dāng)Thread-2線程持有monitor對(duì)象后,就會(huì)把monitor中的owner變量設(shè)置為當(dāng)前線程Thread-2;

(4) 當(dāng)Thread-3線程想要執(zhí)行臨界區(qū)的代碼時(shí),要判斷monitor對(duì)象的屬性O(shè)wner是否為null,如果為null,Thread-3線程就持有了對(duì)象鎖,如果不為null,Thread-3線程就會(huì)放入monitor的EntryList阻塞隊(duì)列中,處于阻塞狀態(tài)Blocked。

(5) 在 Thread-2 上鎖的過(guò)程中,如果Thread-4,Thread-5 也來(lái)執(zhí)行 synchronized(obj),也會(huì)進(jìn)入EntryList BLOCKED ;

(6) Thread-2 執(zhí)行完同步代碼塊的內(nèi)容,就會(huì)釋放鎖,將owner變量置為null,并喚醒EntryList 中阻塞的線程來(lái)競(jìng)爭(zhēng)鎖,競(jìng)爭(zhēng)時(shí)是非公平的 ;

(7) 圖中 WaitSet 中的 Thread-0,Thread-1 是之前獲得過(guò)鎖,但條件不滿足進(jìn)入 WAITING 狀態(tài)的線程,后面講 wait-notify 時(shí)會(huì)分析

2. snychronized同步代碼塊原理

public class TestSynchronized {
    static final Object obj = new Object();
    static int i=0;
    public static void main(String[] args) {
        synchronized (obj){
            i++;
        }
    }
}

將上面的代碼反編譯為字節(jié)碼文件:

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: getstatic     #2         // 獲取obj對(duì)象
         3: dup
         4: astore_1
         5: monitorenter		//將obj對(duì)象的markword置為monitor指針
         6: getstatic     #3                  
         9: iconst_1
        10: iadd
        11: putstatic     #3                  
        14: aload_1
        15: monitorexit			//同步代碼塊正常執(zhí)行時(shí),將obj對(duì)象的markword重置,喚醒EntryList
        16: goto          24
        19: astore_2
        20: aload_1
        21: monitorexit			//同步代碼塊出現(xiàn)異常時(shí),將obj對(duì)象的markword重置,喚醒EntryList
        22: aload_2
        23: athrow
        24: return
      Exception table:
         from    to  target type
             6    16    19   any  //監(jiān)測(cè)6-16行jvm指令,如果出現(xiàn)異常就會(huì)到第19行
            19    22    19   any

這兩條指令的作用,我們直接參考JVM規(guī)范中描述:

monitorenter 指令:

每個(gè)對(duì)象有一個(gè)監(jiān)視器鎖(monitor)。當(dāng)monitor被占用時(shí)就會(huì)處于鎖定狀態(tài),線程執(zhí)行monitorenter指令時(shí)嘗試獲取monitor的所有權(quán),過(guò)程如下:

(1) 如果monitor的進(jìn)入數(shù)為0,則該線程進(jìn)入monitor,然后將進(jìn)入數(shù)設(shè)置為1,該線程即為monitor的所有者

(2) 如果線程已經(jīng)占有該monitor,只是重新進(jìn)入,則進(jìn)入monitor的進(jìn)入數(shù)加1.

(3) 如果其他線程已經(jīng)占用了monitor,則該線程進(jìn)入阻塞狀態(tài),直到monitor的進(jìn)入數(shù)為0,再重新嘗試獲取monitor的所有權(quán)。

monitorexit指令:

執(zhí)行monitorexit的線程必須是持有obj鎖對(duì)象的線程

指令執(zhí)行時(shí),monitor的進(jìn)入數(shù)減1,如果減1后進(jìn)入數(shù)為0,那線程釋放monitor,不再是這個(gè)monitor的所有者。其他被這個(gè)monitor阻塞的線程可以嘗試去獲取這個(gè) monitor 的所有權(quán)。

Synchronized的語(yǔ)義底層是通過(guò)一個(gè)monitor的對(duì)象來(lái)完成,其實(shí)wait/notify等方法也依賴于monitor對(duì)象,這就是為什么只有在同步的塊或者方法中才能調(diào)用wait/notify等方法,否則會(huì)拋出IllegalMonitorStateException的異常的原因。

3. synchronized同步方法原理

public class TestSynchronized {
    static int i=0;
    public synchronized  void add(){
        i++;
    }
}

對(duì)應(yīng)的字節(jié)碼指令:

 public synchronized void add();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field i:I
         3: iconst_1
         4: iadd
         5: putstatic     #2                  // Field i:I
         8: return

從反編譯的結(jié)果來(lái)看,方法的同步并沒(méi)有通過(guò)指令monitorentermonitorexit來(lái)完成不過(guò)相對(duì)于普通方法,其常量池中多了ACC_SYNCHRONIZED標(biāo)示符。JVM就是根據(jù)該標(biāo)示符來(lái)實(shí)現(xiàn)方法的同步的:當(dāng)方法調(diào)用時(shí),調(diào)用指令將會(huì)檢查方法的 ACC_SYNCHRONIZED 訪問(wèn)標(biāo)志是否被設(shè)置,如果設(shè)置了,執(zhí)行線程將先獲取monitor,獲取成功之后才能執(zhí)行方法體,方法執(zhí)行完后再釋放monitor。在方法執(zhí)行期間,其他任何線程都無(wú)法再獲得同一個(gè)monitor對(duì)象。 其實(shí)本質(zhì)上沒(méi)有區(qū)別,只是方法的同步是一種隱式的方式來(lái)實(shí)現(xiàn),無(wú)需通過(guò)字節(jié)碼來(lái)完成。

4. 重量級(jí)鎖的開(kāi)銷

處于ContentionListEntryList、WaitSet中的線程都處于阻塞狀態(tài),線程的阻塞或者喚醒都需要操作系統(tǒng)來(lái)幫忙,Linux內(nèi)核下采用pthread_mutex_lock系統(tǒng)調(diào)用實(shí)現(xiàn),進(jìn)程需要從用戶態(tài)切換到內(nèi)核態(tài)。

用戶態(tài)是應(yīng)用程序運(yùn)行的空間,為了能訪問(wèn)到內(nèi)核管理的資源(例如CPU、內(nèi)存、I/O),可以通過(guò)內(nèi)核態(tài)所提供的訪問(wèn)接口實(shí)現(xiàn),這些接口就叫系統(tǒng)調(diào)用。

pthread_mutex_lock系統(tǒng)調(diào)用是內(nèi)核態(tài)為用戶態(tài)進(jìn)程提供的Linux內(nèi)核態(tài)下互斥鎖的訪問(wèn)機(jī)制,所以使用pthread_mutex_lock系統(tǒng)調(diào)用時(shí),進(jìn)程需要從用戶態(tài)切換到內(nèi)核態(tài),而這種切換是需要消耗很多時(shí)間的,有可能比用戶執(zhí)行代碼的時(shí)間還要長(zhǎng)。

由于JVM輕量級(jí)鎖使用CAS進(jìn)行自旋搶鎖,這些CAS操作都處于用戶態(tài)下,進(jìn)程不存在用戶態(tài)和內(nèi)核態(tài)之間的運(yùn)行切換,因此JVM輕量級(jí)鎖開(kāi)銷較小。而JVM重量級(jí)鎖使用了Linux內(nèi)核態(tài)下的互斥鎖,這是重量級(jí)鎖開(kāi)銷很大的原因。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容! 

相關(guān)文章

最新評(píng)論