1. 程式人生 > 實用技巧 >Java多執行緒 - synchronized 底層實現原理

Java多執行緒 - synchronized 底層實現原理

總結

每個物件有一個監視器鎖(monitor)。下面兩個方式,本質上都是通過監視器鎖(monitor)來控制

  • 同步程式碼塊是通過 monitorenter 和monitorexit 指令獲取執行緒的執行權
  • 同步方法通過加ACC_SYNCHRONIZED 標識實現執行緒的執行權的控制

"同步程式碼塊"的原理

我們先通過反編譯下面的程式碼來看看Synchronized是如何實現對程式碼塊進行同步的:

package com.paddx.test.concurrent;

public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            System.out.println("Method 1 start");
        }
    }
}

反編譯結果:

關於這兩條指令的作用,我們直接參考JVM規範中描述:

monitorenter

這段話的大概意思為:

每個物件有一個監視器鎖(monitor)。當monitor被佔用時就會處於鎖定狀態,執行緒執行monitorenter指令時嘗試獲取monitor的所有權,過程如下:

1、如果monitor的進入數為0,則該執行緒進入monitor,然後將進入數設定為1,該執行緒即為monitor的所有者。

2、如果執行緒已經佔有該monitor,只是重新進入,則進入monitor的進入數加1.

3.如果其他執行緒已經佔用了monitor,則該執行緒進入阻塞狀態,直到monitor的進入數為0,再重新嘗試獲取monitor的所有權。

monitorexit 

這段話的大概意思為:

執行monitorexit的執行緒必須是objectref所對應的monitor的所有者。

指令執行時,monitor的進入數減1,如果減1後進入數為0,那執行緒退出monitor,不再是這個monitor的所有者。其他被這個monitor阻塞的執行緒可以嘗試去獲取這個 monitor 的所有權。

  

通過這兩段描述,我們應該能很清楚的看出Synchronized的實現原理,Synchronized的語義底層是通過一個monitor的物件來完成,其實wait/notify等方法也依賴於monitor物件,這就是為什麼只有在同步的塊或者方法中才能呼叫wait/notify等方法,否則會丟擲java.lang.IllegalMonitorStateException的異常的原因。

"同步方法"的原理

我們再來看一下同步方法的反編譯結果:

原始碼:

package com.paddx.test.concurrent;

public class SynchronizedMethod {
    public synchronized void method() {
        System.out.println("Hello World!");
    }
}

反編譯結果:

  從反編譯的結果來看,方法的同步並沒有通過指令monitorenter和monitorexit來完成(理論上其實也可以通過這兩條指令來實現),不過相對於普通方法,其常量池中多了ACC_SYNCHRONIZED標示符。JVM就是根據該標示符來實現方法的同步的:當方法呼叫時,呼叫指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設定,如果設定了,執行執行緒將先獲取monitor,獲取成功之後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其他任何執行緒都無法再獲得同一個monitor物件。 其實本質上沒有區別,只是方法的同步是一種隱式的方式來實現,無需通過位元組碼來完成。