Java提供了多種機制實現多執行緒之間有需要同步執行的場景需求。其中最基本的是Synchronized ,實現上使用物件監視器( Monitor )。
Java中的每個物件都是與執行緒可以鎖定或解鎖的物件監視器( Monitor )關聯。在同一時間只有一個執行緒可以在物件監視器( Monitor )上保持鎖定。任何其他執行緒試圖鎖定物件監視器( Monitor )都會被阻止,直到它們可以獲得該監視器上的鎖定。
Synchronized 的作用範圍,依據鎖定的物件(object、this、class)、使用方式,可以分成五種情況。如果按照JVM位元組碼的區別,也可以分成兩種形式:程式碼塊(monitorenter、monitorexit)、函數(ACC_SYNCHRONIZED)。
雖然可以按照不同維度來劃分 Synchronized 但本身機制是一樣的,無論是 Synchronized 函數/程式碼塊,都是通過物件監視器( Monitor )來實現。無論是this、class、object本質上都是一個物件,區別無非代表的是當前範例、類、一般範例,它們都有著物件監視器( Monitor )。
在HotSpot虛擬機器器中,物件監視器( Monitor ) 具體的實現類就是 ObjectMonitor(C++)。
在使用/分析 Synchronized 同步是否有效正確的時候,只需要分析需要的同步塊是否作用在同一個物件監視器( Monitor )上。換一種描述,是否作用在同一個物件(Object)上,這裡(Object)可以是this、object、class。
下面分別按照Synchronized 程式碼塊、Synchronized 函數維度來進行詳細介紹。
Synchronized 程式碼塊的一般使用形式:synchronized ( Expression ) Block 。
Expression 必須是一個物件,可以是class、this、object,不能是原始型別(int、float...);否則編譯的時候就會報錯。如果 Expression 是null,會丟擲NullPointerException 的異常。
Block表示一段邏輯程式碼,執行邏輯程式碼前會鎖定Expression 的物件監視器( Monitor )。如果正常執行完成後,物件監視器( Monitor )會被釋放;如果執行期間異常/中斷了同樣的也會釋放物件監視器( Monitor )。先加鎖確保其他執行緒無法進入執行,所以Synchronized 是悲觀鎖,JVM指令上使用monitorenter、monitorexit 來進行相關實現。
在位元組碼指令裡可以也可以看到有兩個monitorexit ,一個是正常執行後的釋放;另一個是在異常(athrow)丟擲前的釋放。同一個執行緒可以多次進入被鎖定的相同物件監視器( Monitor ),所以Synchronized 是可重入鎖。
Synchronized函數在同步原理上同 Synchronized程式碼塊是沒有區別的,都是通過鎖定物件監視器( Monitor );區別在於這裡的物件是隱藏了起來。同樣的支援可重入。
如果是靜態方法(static),鎖定的物件是這個方法所在的class object 物件。
如果是普通的方法,鎖定的是this(當前範例)物件。
編譯成JVM位元組碼的時候,函數描述上會標識ACC_SYNCHRONIZED ,並不會在函數程式碼塊中顯示的使用monitorenter、monitorexit指令。
在呼叫函數前鎖定物件監視器( Monitor ),完成執行後釋放物件監視器( Monitor )。無論函數是否有顯性的丟擲/處理異常,如果有異常中斷丟擲前也會自動的釋放鎖定的物件監視器( Monitor )。
Synchronized同步一直也在進行優化,也是跟隨著JDK新理念一起發展。比如偏向鎖、輕量鎖、重量鎖、適應性自旋等等機制。不同的JDK版本,不同的JVM可能都有所不同。
在HotSpot虛擬機器器中,拋開鎖升級、自適應等機制;基本原理是執行緒通過 CAS搶佔物件監視器( Monitor ) _Owner來實現鎖,沒有搶佔的會進入 _EntryList 來進行放置。當然, 執行緒執行/中斷釋放_Owner後,_EntryList並不是簡單按照FIFO來進行選擇執行不會保證公平性,所以Synchronized是非公平鎖。
圖中_WaitSet沒有體現用途,但其是很重要的一個結構, 用於當 _Owner 執行執行緒中斷時,執行緒將會寫入。值得注意獲取到鎖之後才能中斷,等待鎖時不可中斷。當相關執行緒被喚醒後,會採有不同的策略重新回到_EntryList 或者 參與CAS競爭 _Owner,這裡存線上程上下文切換的可能。
詳細可以檢視相關原始碼:
http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src
在最後綜述下Synchronized 特性:悲觀鎖、可重入鎖、非公平鎖。
歡迎長期關注公眾號/頭條號(Java研究者)