Java併發編程-synchronized底層原理

synchronized底層原理與Monitor密切相關java

1.Java對象頭

以 32 位虛擬機爲例
普通對象算法

對象的類型,如Student類型,Teacher類型等是由KlassWord來表示的,它是一個指針,指向了對象所從屬的Class。即找到類。
其中 Mark Word 結構爲數組

其中,age表示垃圾回收時的分代年齡,具體可見分代收集算法。年齡超過必定歲數就從倖存區調入到老年代。最後兩位,biased_lock和01表示偏向鎖和加鎖狀態。其它表示在不一樣狀態下每一位所表明的含義。Normal狀態表示對象的正常狀態。對它加各類所的時候,其值一般會改變。 數組對象markdown

注意:因此,這裏咱們也能夠想到,在一個Integer對象裏,要保存8個字節的對象頭,還有4個字節的int類型的數據。oop

總共12字節,而基本數據類型int則只需4個字節。在內存敏感的狀況下,建議用基本類型。 2.Monitor(鎖)工做原理 synchronized底層原理能夠用Monitor工做原理來解釋 Monitor被翻譯爲監視器或者管程 每一個 Java 對象均可以關聯一個 Monitor 對象,若是使用 synchronized 給對象上鎖(重量級)以後,該對象頭的 Mark Word 中就被設置指向 Monitor 對象的指針學習

當咱們線程2執行以下代碼時:this

synchronized(this){
//處理相關業務
}
複製代碼

1.Thread2線程執行上述代碼時,當前對象this會被上一把鎖,這是一把重量級鎖,this對象頭的Mark Word字段指向了操做系統建立的Monitor對象引用地址 MarkWord在沒有加任何鎖的時候,即Normal狀態,標記位爲01,一旦獲取了鎖,就會嘗試找一個monitor與之關聯,而後把最後兩位也即標記位從01改成10,而且把前面的全部位改爲指向monitor對象的指針,佔用30位。此時因爲只有Thread2線程,因此成功獲取了鎖。理所應當的成爲了monitor對象的owner。 owner表示Monitor鎖的持有者,並且同一個時刻只能有一個owner spa

2.Monitor對象只能有一個owner,此時若是有其它線程如Thread-3或Thread-4等線程要獲取這把鎖就要進入Monitor對象的堵塞隊列EntryList中等待Thread2釋放鎖。 EntryList能夠理解位阻塞隊列或等待隊列。一直等待到其它線程釋放了owner的全部權  操作系統

3.等待鎖資源被釋放後,Thread-3或Thread-4會互相競爭鎖資源,並不能保證誰獲取到鎖,最終仍是有CPU來決定。 .net

4.Monitor對象的WaitSet存放的是,獲取到鎖的線程,可是因爲其它一些緣由致使線程進入Waiting狀態,又釋放了鎖資源,待介紹 一樣地,對於下圖

1.剛開始 Monitor 中 Owner 爲 null 

2.當 Thread-2 執行 synchronized(obj) 就會將 Monitor 的全部者 Owner 置爲 Thread-2,Monitor中只能有一個 Owner 

3.在 Thread-2 上鎖的過程當中,若是 Thread-3,Thread-4,Thread-5 也來執行 synchronized(obj),就會進入EntryList BLOCKED

4.Thread-2 執行完同步代碼塊的內容,而後喚醒 EntryList 中等待的線程來競爭鎖,競爭的時是非公平的 

5.圖中 WaitSet 中的 Thread-0,Thread-1 是以前得到過鎖,但條件不知足進入 WAITING 狀態的線程,後面講wait-notify 時會分析

 synchronized 必須是進入同一個對象的 monitor 纔有上述的效果 

不加 synchronized 的對象不會關聯監視器,不聽從以上規則 

3.字節碼角度理解synchronized底層工做原理

public class synchronized1 {
    static final Object lock=new Object();
    static int counter=0;
    public static void main(String[] args){
        synchronized (lock){
            counter++;
        }
    }
} 
複製代碼

反編譯成字節碼,具體反編譯過程及字節碼指令介紹能夠參看JVM學習-字節碼指令
字節碼以下:

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                  //<-lock引用(synchronized開始)
         3: dup
         4: astore_1						  //lock引->slot1
         5: monitorenter					  //將lock對象Markword置爲monitor指針
         6: getstatic     #3                  // Field counter:I
         9: iconst_1
        10: iadd
        11: putstatic     #3                  // Field counter:I
        14: aload_1							  //<-lock引用,拿到剛開astore1剛纔存儲的臨時變量
        15: monitorexit						  //將lock對象MarkWord重置,喚醒EntryList.從新設置markword其它字段,讓entryList中正在等待的線程競爭鎖
        16: goto          24				 
   /*
   若是同步代碼塊中發生了異常,而後就處理下列代碼,即19-23行。
   最下面的Exception table表第一行時監測6-16行是否發生了異常,即同步代碼塊中的代碼。
   若是發生了異常,就跳轉到19行執行。
   先把異常對象存儲進來,而後根據對象引用地址找到monitor,而後也是作一些善後的工做。
   把monitor中的狀態還原,並喚醒entrylist中的其它線程      
   */
        19: astore_2
        20: aload_1
        21: monitorexit
        22: aload_2
        23: athrow
        24: return
      Exception table:
         from    to  target type
             6    16    19   any
            19    22    19   any 
複製代碼

看完四件事❤️

若是你以爲這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:

  1. 點贊,轉發,有大家的 『點贊和評論』,纔是我創造的動力。

  2. 關注公衆號 『 JavaAC 』,不按期分享原創知識。

  3. 同時能夠期待後續文章ing🚀

  4. .關注後掃碼便可獲取學習資料包

相關文章
相關標籤/搜索