Java併發(十九)----Monitor原理及Synchronized原理

|舊市拾荒|發表於2023-12-17

1、Java 物件頭

以 32 位虛擬機器為例

普通物件

|--------------------------------------------------------------|
|                     Object Header (64 bits)                  |
|------------------------------------|-------------------------|
|        Mark Word (32 bits)         |    klass Word (32 bits) |
|------------------------------------|-------------------------|

陣列物件

|---------------------------------------------------------------------------------|
|                                 Object Header (96 bits)                         |
|--------------------------------|-----------------------|------------------------|
|        Mark Word(32bits)       |    Klass Word(32bits) |  array length(32bits)  |
|--------------------------------|-----------------------|------------------------|

其中 Mark Word 結構為

|-------------------------------------------------------|--------------------|
|                  Mark Word (32 bits)                  |       State        |
|-------------------------------------------------------|--------------------|
|  hashcode:25         | age:4 | biased_lock:0 | 01     |       Normal       |
|-------------------------------------------------------|--------------------|
|  thread:23 | epoch:2 | age:4 | biased_lock:1 | 01     |       Biased       |
|-------------------------------------------------------|--------------------|
|               ptr_to_lock_record:30          | 00     | Lightweight Locked |
|-------------------------------------------------------|--------------------|
|               ptr_to_heavyweight_monitor:30  | 10     | Heavyweight Locked |
|-------------------------------------------------------|--------------------|
|                                              | 11     |    Marked for GC   |
|-------------------------------------------------------|--------------------|

64 位虛擬機器 Mark Word

|--------------------------------------------------------------------|--------------------|
|                        Mark Word (64 bits)                         |       State        |
|--------------------------------------------------------------------|--------------------|
| unused:25 | hashcode:31 | unused:1 | age:4 | biased_lock:0 | 01    |       Normal       |
|--------------------------------------------------------------------|--------------------|
| thread:54 | epoch:2     | unused:1 | age:4 | biased_lock:1 | 01    |       Biased       |
|--------------------------------------------------------------------|--------------------|
|             ptr_to_lock_record:62                          | 00    | Lightweight Locked |
|--------------------------------------------------------------------|--------------------|
|             ptr_to_heavyweight_monitor:62                  | 10    | Heavyweight Locked |
|--------------------------------------------------------------------|--------------------|
|                                                            | 11    |    Marked for GC   |
|--------------------------------------------------------------------|--------------------|

參考資料

https://stackoverflow.com/questions/26357186/what-is-in-java-object-header

2、Monitor 原理

Monitor 被翻譯為監視器管程

每個 Java 物件都可以關聯一個 Monitor 物件,如果使用 synchronized 給物件上鎖(重量級)之後,該物件頭的 Mark Word 中就被設定指向 Monitor 物件的指標

Monitor 結構如下

Java併發(十九)----Monitor原理及Synchronized原理

  • 剛開始 Monitor 中 Owner 為 null

  • 當 Thread-2 執行 synchronized(obj) 就會將 Monitor 的所有者 Owner 置為 Thread-2,Monitor中只能有一個 Owner

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

  • Thread-2 執行完同步程式碼塊的內容,然後喚醒 EntryList(阻塞佇列) 中等待的執行緒來競爭鎖,競爭是非公平的

  • 圖中 WaitSet 中的 Thread-0,Thread-1 是之前獲得過鎖,但條件不滿足進入 WAITING 狀態的執行緒,後面講 wait-notify 時會分析

注意:

  • synchronized 必須是進入同一個物件的 monitor 才有上述的效果

  • 不加 synchronized 的物件不會關聯監視器,不遵從以上規則

3、synchronized 原理

static final Object lock = new Object();
static int counter = 0;
​
public static void main(String[] args) {
    synchronized (lock) {
        counter++;
    }
}

對應的位元組碼為

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引用 -> slot 1
         5: monitorenter                      // 將 lock物件 MarkWord 置為 Monitor 指標
         6: getstatic     #3                  // <- i
         9: iconst_1                          // 準備常數 1
        10: iadd                              // +1
        11: putstatic     #3                  // -> i
        14: aload_1                           // <- lock引用
        15: monitorexit                       // 將 lock物件 MarkWord 重置, 喚醒 EntryList
        16: goto          24                  // 19-23 為異常處理
        19: astore_2                          // e -> slot 2 
        20: aload_1                           // <- lock引用
        21: monitorexit                       // 將 lock物件 MarkWord 重置, 喚醒 EntryList
        22: aload_2                           // <- slot 2 (e)
        23: athrow                            // throw e
        24: return
      Exception table:                      // 異常檢測
         from    to  target type
             6    16    19   any            // 6-16 行出現異常 目標為19行
            19    22    19   any            // 19-22 行出現異常 目標為19行
      LineNumberTable:
        line 8: 0
        line 9: 6
        line 10: 14
        line 11: 24
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      25     0  args   [Ljava/lang/String;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 19
          locals = [ class "[Ljava/lang/String;", class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

注意

方法級別的 synchronized 不會在位元組碼指令中有所體現

 

相關文章