synchronized底層原理與Monitor密切相關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
複製代碼
若是你以爲這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:
點贊,轉發,有大家的 『點贊和評論』,纔是我創造的動力。
關注公衆號 『 JavaAC 』,不按期分享原創知識。
同時能夠期待後續文章ing🚀
.關注後掃碼便可獲取學習資料包