synchronized底層原理與Monitor密切相關
1.Java物件頭
以 32 位虛擬機為例
普通物件

物件的型別,如Student型別,Teacher型別等是由KlassWord來表示的,它是一個指標,指向了物件所從屬的Class,即找到類,
其中 Mark Word 結構為

其中,age表示垃圾回收時的分代年齡,具體可見分代收集演算法,年齡超過一定歲數就從幸存區調入到老年代,最后兩位,biased_lock和01表示偏向鎖和加鎖狀態,其它表示在不同狀態下每一位所代表的含義,Normal狀態表示物件的正常狀態,對它加各種所的時候,其值通常會改變,
陣列物件

注意:所以,這里我們也可以想到,在一個Integer物件里,要保存8個位元組的物件頭,還有4個位元組的int型別的資料,總共12位元組,而基本資料型別int則只需4個位元組,在記憶體敏感的情況下,建議用基本型別,
2.Monitor(鎖)作業原理
synchronized底層原理可以用Monitor作業原理來解釋
Monitor被翻譯為監視器或者管程
每個 Java 物件都可以關聯一個 Monitor 物件,如果使用 synchronized 給物件上鎖(重量級)之后,該物件頭的
Mark Word 中就被設定指向 Monitor 物件的指標

當我們執行緒2執行如下代碼時:
synchronized(this){
//處理相關業務
}
1.Thread2執行緒執行上述代碼時,當前物件this會被上一把鎖,這是一把重量級鎖,this物件頭的Mark Word欄位指向了作業系統創建的Monitor物件參考地址
MarkWord在沒有加任何鎖的時候,即Normal狀態,標記位為01,一旦獲取了鎖,就會嘗試找一個monitor與之關聯,然后把最后兩位也即標記位從01改為10,并且把前面的所有位改成指向monitor物件的指標,占用30位,此時由于只有Thread2執行緒,所以成功獲取了鎖,理所應當的成為了monitor物件的owner,
owner表示Monitor鎖的持有者,而且同一個時刻只能有一個owner
2.Monitor物件只能有一個owner,此時如果有其它執行緒如Thread-3或Thread-4等執行緒要獲取這把鎖就要進入Monitor物件的堵塞佇列EntryList中等待Thread2釋放鎖,
EntryList可以理解位阻塞佇列或等待佇列,一直等待到其它執行緒釋放了owner的所有權
3.等待鎖資源被釋放后,Thread-3或Thread-4會互相競爭鎖資源,并不能保證誰獲取到鎖,最侄訓是有CPU來決定,
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
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/257094.html
標籤:java
下一篇:動態代理
