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

synchronized背后的monitor鎖實(shí)現(xiàn)詳解

 更新時(shí)間:2022年09月07日 08:51:23   作者:會(huì)飛的湯姆貓  
這篇文章主要為大家介紹了synchronized背后的monitor鎖實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

獲取和釋放 monitor 鎖的時(shí)機(jī)

本文我們研究下 synchronized 背后的 monitor 鎖。

我們都知道,最簡(jiǎn)單的同步方式就是利用 synchronized 關(guān)鍵字來修飾代碼塊或者修飾一個(gè)方法,那么這部分被保護(hù)的代碼,在同一時(shí)刻就最多只有一個(gè)線程可以運(yùn)行,而 synchronized 的背后正是利用 monitor 鎖實(shí)現(xiàn)的。所以首先我們來看下獲取和釋放 monitor 鎖的時(shí)機(jī),每個(gè) Java 對(duì)象都可以用作一個(gè)實(shí)現(xiàn)同步的鎖,這個(gè)鎖也被稱為內(nèi)置鎖或 monitor 鎖,獲得 monitor 鎖的唯一途徑就是進(jìn)入由這個(gè)鎖保護(hù)的同步代碼塊或同步方法,線程在進(jìn)入被 synchronized 保護(hù)的代碼塊之前,會(huì)自動(dòng)獲取鎖,并且無論是正常路徑退出,還是通過拋出異常退出,在退出的時(shí)候都會(huì)自動(dòng)釋放鎖。

我們首先來看一個(gè) synchronized 修飾方法的代碼的例子:

public synchronized void method() {
    method body
}

我們看到 method() 方法是被 synchronized 修飾的,為了方便理解其背后的原理,我們把上面這段代碼改寫為下面這種等價(jià)形式的偽代碼。

public void method() {
    this.intrinsicLock.lock();
    try{
        method body
    }
    finally {
        this.intrinsicLock.unlock();
    }
}

在這種寫法中,進(jìn)入 method 方法后,立刻添加內(nèi)置鎖,并且用 try 代碼塊把方法保護(hù)起來,最后用 finally 釋放這把鎖,這里的 intrinsicLock 就是 monitor 鎖。經(jīng)過這樣的偽代碼展開之后,相信你對(duì) synchronized 的理解就更加清晰了。

用 javap 命令查看反匯編的結(jié)果

JVM 實(shí)現(xiàn) synchronized 方法和 synchronized 代碼塊的細(xì)節(jié)是不一樣的,下面我們就分別來看一下兩者的實(shí)現(xiàn)。

同步代碼塊

首先我們來看下同步代碼塊的實(shí)現(xiàn),如代碼所示。

public class SynTest {
    public void synBlock() {
        synchronized (this) {
            System.out.println("lagou");
        }
    }
}

在 SynTest 類中的 synBlock 方法,包含一個(gè)同步代碼塊,synchronized 代碼塊中有一行代碼打印了 lagou 字符串,下面我們來通過命令看下 synchronized 關(guān)鍵字到底做了什么事情:首先用 cd 命令切換到 SynTest.java 類所在的路徑,然后執(zhí)行 javac SynTest.java,于是就會(huì)產(chǎn)生一個(gè)名為 SynTest.class 的字節(jié)碼文件,然后我們執(zhí)行 javap -verbose SynTest.class,就可以看到對(duì)應(yīng)的反匯編內(nèi)容。

關(guān)鍵信息如下:

  public void synBlock();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         7: ldc           #3                      // String lagou
         9: invokevirtual #4               // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        12: aload_1
        13: monitorexit
        14: goto          22
        17: astore_2
        18: aload_1
        19: monitorexit
        20: aload_2
        21: athrow
        22: return

從里面可以看出,synchronized 代碼塊實(shí)際上多了 monitorenter 和 monitorexit 指令,標(biāo)紅的第3、13、19行指令分別對(duì)應(yīng)的是 monitorenter 和 monitorexit。這里有一個(gè) monitorenter,卻有兩個(gè) monitorexit 指令的原因是,JVM 要保證每個(gè) monitorenter 必須有與之對(duì)應(yīng)的 monitorexit,monitorenter 指令被插入到同步代碼塊的開始位置,而 monitorexit 需要插入到方法正常結(jié)束處和異常處兩個(gè)地方,這樣就可以保證拋異常的情況下也能釋放鎖

可以把執(zhí)行 monitorenter 理解為加鎖,執(zhí)行 monitorexit 理解為釋放鎖,每個(gè)對(duì)象維護(hù)著一個(gè)記錄著被鎖次數(shù)的計(jì)數(shù)器。未被鎖定的對(duì)象的該計(jì)數(shù)器為 0,我們來具體看一下 monitorenter 和 monitorexit 的含義:

  • monitorenter

執(zhí)行 monitorenter 的線程嘗試獲得 monitor 的所有權(quán),會(huì)發(fā)生以下這三種情況之一:

a. 如果該 monitor 的計(jì)數(shù)為 0,則線程獲得該 monitor 并將其計(jì)數(shù)設(shè)置為 1。然后,該線程就是這個(gè) monitor 的所有者。

b. 如果線程已經(jīng)擁有了這個(gè) monitor ,則它將重新進(jìn)入,并且累加計(jì)數(shù)。

c. 如果其他線程已經(jīng)擁有了這個(gè) monitor,那個(gè)這個(gè)線程就會(huì)被阻塞,直到這個(gè) monitor 的計(jì)數(shù)變成為 0,代表這個(gè) monitor 已經(jīng)被釋放了,于是當(dāng)前這個(gè)線程就會(huì)再次嘗試獲取這個(gè) monitor。

  • monitorexit monitorexit 的作用是將 monitor 的計(jì)數(shù)器減 1,直到減為 0 為止。代表這個(gè) monitor 已經(jīng)被釋放了,已經(jīng)沒有任何線程擁有它了,也就代表著解鎖,所以,其他正在等待這個(gè) monitor 的線程,此時(shí)便可以再次嘗試獲取這個(gè) monitor 的所有權(quán)。

同步方法

從上面可以看出,同步代碼塊是使用 monitorenter 和 monitorexit 指令實(shí)現(xiàn)的。而對(duì)于 synchronized 方法,并不是依靠 monitorenter 和 monitorexit 指令實(shí)現(xiàn)的,被 javap 反匯編后可以看到,synchronized 方法和普通方法大部分是一樣的,不同在于,這個(gè)方法會(huì)有一個(gè)叫作 ACC_SYNCHRONIZED 的 flag 修飾符,來表明它是同步方法。

同步方法的代碼如下所示。

public synchronized void synMethod() {
}

對(duì)應(yīng)的反匯編指令如下所示。

  public synchronized void synMethod();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 16: 0

可以看出,被 synchronized 修飾的方法會(huì)有一個(gè) ACC_SYNCHRONIZED 標(biāo)志。當(dāng)某個(gè)線程要訪問某個(gè)方法的時(shí)候,會(huì)首先檢查方法是否有 ACC_SYNCHRONIZED 標(biāo)志,如果有則需要先獲得 monitor 鎖,然后才能開始執(zhí)行方法,方法執(zhí)行之后再釋放 monitor 鎖。其他方面, synchronized 方法和剛才的 synchronized 代碼塊是很類似的,例如這時(shí)如果其他線程來請(qǐng)求執(zhí)行方法,也會(huì)因?yàn)闊o法獲得 monitor 鎖而被阻塞。

好了,本文的內(nèi)容就全部講完了,我們講解了獲取和釋放 monitor 的時(shí)機(jī),以及被 synchronized 修飾的等價(jià)代碼,然后我們還利用 javac 和 javap 命令查看了 synchronized 代碼塊以及 synchronized 方法所對(duì)應(yīng)的的反匯編指令,其中同步代碼塊是利用 monitorenter 和 monitorexit 指令實(shí)現(xiàn)的,而同步方法則是利用 flags 實(shí)現(xiàn)的。

更多關(guān)于synchronized monitor鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java如何處理圖片保存之后變紅色的問題

    Java如何處理圖片保存之后變紅色的問題

    這篇文章主要介紹了Java如何處理圖片保存之后變紅色的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • 詳解Mybatis是如何解析配置文件的

    詳解Mybatis是如何解析配置文件的

    這篇文章主要介紹了詳解Mybatis是如何解析配置文件的,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • SpringBoot?@DS注解實(shí)現(xiàn)多數(shù)據(jù)源配置以及問題解決辦法

    SpringBoot?@DS注解實(shí)現(xiàn)多數(shù)據(jù)源配置以及問題解決辦法

    這篇文章主要給大家介紹了關(guān)于SpringBoot?@DS注解實(shí)現(xiàn)多數(shù)據(jù)源配置以及問題解決辦法,所謂多數(shù)據(jù)源就是一個(gè)Java EE項(xiàng)目中采用了不同數(shù)據(jù)庫(kù)實(shí)例中的多個(gè)庫(kù),或者是同一個(gè)數(shù)據(jù)庫(kù)實(shí)例中的多個(gè)不同庫(kù),需要的朋友可以參考下
    2023-11-11
  • Springboot詳解RocketMQ實(shí)現(xiàn)廣播消息流程

    Springboot詳解RocketMQ實(shí)現(xiàn)廣播消息流程

    RocketMQ作為一款純java、分布式、隊(duì)列模型的開源消息中間件,支持事務(wù)消息、順序消息、批量消息、定時(shí)消息、消息回溯等,本篇我們了解如何實(shí)現(xiàn)廣播消息
    2022-06-06
  • Java 十大排序算法之歸并排序刨析

    Java 十大排序算法之歸并排序刨析

    歸并排序是采用分治法的一個(gè)非常典型的應(yīng)用。先使每個(gè)子序列有序,再使子序列段間有序,也就是將已有的子序列合并,得到完全有序的序列;如果將兩個(gè)有序表合并成一個(gè)有序表,稱為二路歸并
    2021-11-11
  • java實(shí)現(xiàn)讀取、刪除文件夾下的文件

    java實(shí)現(xiàn)讀取、刪除文件夾下的文件

    本文給大家分享的是java實(shí)現(xiàn)讀取、刪除文件夾下的文件,其中File.delete()用于刪除“某個(gè)文件或者空目錄”!所以要?jiǎng)h除某個(gè)目錄及其中的所有文件和子目錄,要進(jìn)行遞歸刪除,有需要的小伙伴可以參考下。
    2015-05-05
  • java反射機(jī)制示例

    java反射機(jī)制示例

    這篇文章主要介紹了java反射機(jī)制示例,需要的朋友可以參考下
    2014-04-04
  • @Autowired與@Resource在實(shí)現(xiàn)對(duì)象注入時(shí)的區(qū)別

    @Autowired與@Resource在實(shí)現(xiàn)對(duì)象注入時(shí)的區(qū)別

    這篇文章主要介紹了@Autowired與@Resource在實(shí)現(xiàn)對(duì)象注入時(shí)的區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2023-04-04
  • Java采用setAsciiStream方法檢索數(shù)據(jù)庫(kù)指定內(nèi)容實(shí)例解析

    Java采用setAsciiStream方法檢索數(shù)據(jù)庫(kù)指定內(nèi)容實(shí)例解析

    這篇文章主要介紹了Java采用setAsciiStream方法檢索數(shù)據(jù)庫(kù)指定內(nèi)容,是比較實(shí)用的功能,需要的朋友可以參考下
    2014-08-08
  • 詳解如何在項(xiàng)目中應(yīng)用SpringSecurity權(quán)限控制

    詳解如何在項(xiàng)目中應(yīng)用SpringSecurity權(quán)限控制

    本文主要介紹了如何在項(xiàng)目中應(yīng)用SpringSecurity權(quán)限控制,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06

最新評(píng)論