synchronized底層原理
通過反編譯class文件,可以看到synchronized最關鍵的部分是monitor對象。
又因為synchronized關鍵字使用的方法不同,可以將monitor對象使用分為以下兩種情況。
-
synchronized放在方法簽名上
public synchronized void method(){ }
這時候在反編譯文件里,該方法的ACC_SYNCHRONIZED訪問標志位會被標記
-
synchronized作為對象鎖
public void method(){ synchronized(new Object()){ } }
這時候ACC_SYNCHRONIZED並不會被標記,但是會執行monitorenter和monitorexit命令,從而實現同步。
- 可以看到雖然只有一個monitorenter但是有兩個monitorexit,這是因為有兩種情況可以讓當前線程放棄鎖,即
- 當前synchronized代碼塊運行完
- 發生中斷
monitor對象
其實以上兩種方法都是相同的,ACC_SYNCHRONIZED標志位是隱式調用了monitor對象而已。下面來說一下monitor對象以及它是如何實現運作的。
monitor的數據結構
關於存儲的monitor對象有以下三種可能,對應不同的synchronzied使用方式,
-
synchronzied修飾普通方法——>鎖的是this,也就是調用當前方法的實例對象
-
synchronized修飾代碼塊——>鎖的是synchronized后面括號里的方法
-
synchronized修飾static方法——>鎖的是類的.class對象
關於面試的時候會問的各種情況下會不會同步執行,牢記一點
鎖的對象相同才會同步執行
鎖的對象相同才會同步執行
鎖的對象相同才會同步執行
也就是說,類鎖對普通方法鎖是不存在覆蓋的,下面兩個方法不會同步執行
public synchronized void method1(){ } public static synchronized void method2(){ }
monitor運行機制
如果一個線程運行到一個同步代碼塊,如果線程進入數為0,則該線程可擁有此monitor對象的鎖,遇到monitorenter,進入數+1,遇到monitorexit,進入數-1。
如果目前線程進入數不為0,則當前線程不能獲得此monitor對象的鎖,需要等待。
synchronized的可重入性,不可中斷性是如何保障的呢?
monitor對象的線程進入數不是0和1,如果發生重入,進入數+1即可。
不可中斷性:一個線程獲取鎖后,其他線程必須阻塞或等待,不能搶占,按照上面的運行機制,必須線程進入數=0其他線程才能獲得鎖,因而,不可中斷性實現。
另外,有的面試題會問非同步方法和同步方法同時調用會不會同步執行,答案是不會。
因為,非同步方法執行時不會去考慮線程進入數以及獲得鎖一系列流程,直接開始執行,怎么可能同步執行呢