Synchronized關鍵字

一個暱稱而已T發表於2017-11-21

synchronized關鍵字可以用於修飾程式碼塊或者方法,其兩種實現途徑的原理又是不一樣的。

以下是兩者實現原理的簡單說明。

同步程式碼塊:monitorenter指令插入到同步程式碼塊的開始位置,monitorexit指令插入到同步程式碼塊的結束位置,JVM需要保證每一個monitorenter都有一個monitorexit與之相對應。任何物件都有一個monitor與之相關聯,當且一個monitor被持有之後,他將處於鎖定狀態。執行緒執行到monitorenter指令時,將會嘗試獲取物件所對應的monitor所有權,即嘗試獲取物件的鎖; 


同步方法:synchronized方法則會被翻譯成普通的方法呼叫和返回指令如:invokevirtual、areturn指令,在VM位元組碼層面並沒有任何特別的指令來實現被synchronized修飾的方法,而是在Class檔案的方法表中將該方法的access_flags欄位中的synchronized標誌位置1,表示該方法是同步方法並使用呼叫該方法的物件或該方法所屬的Class在JVM的內部物件表示Klass做為鎖物件。

其中Klass的概念可以參見:【理解HotSpot虛擬機器】物件在jvm中的表示:OOP-Klass模型

兩者雖然實現細節不同,但本質上都是對一個物件的監視器(monitor)的獲取。任意一個物件都擁有自己的監視器,當同步程式碼塊或同步方法時,執行方法的執行緒必須先獲得該物件的監視器才能進入同步塊或同步方法,沒有獲取到監視器的執行緒將會被阻塞,並進入同步佇列,狀態變為BLOCKED。當成功獲取監視器的執行緒釋放了鎖後,會喚醒阻塞在同步佇列的執行緒,使其重新嘗試對監視器的獲取。


具體的使用場景:

1、

public synchronized void method1
鎖住的是該物件,類的其中一個例項,當該物件(僅僅是這一個物件)在不同執行緒中執行這個同步方法時,執行緒之間會形成互斥。達到同步效果,但如果不同執行緒同時對該類的不同物件執行這個同步方法時,則執行緒之間不會形成互斥,因為他們擁有的是不同的鎖。

2、

synchronized(this){ //TODO }
同1

3、

public synchronized static void method3
鎖住的是該類,當所有該類的物件(多個物件)在不同執行緒中呼叫這個static同步方法時,執行緒之間會形成互斥,達到同步效果,但如果多個執行緒同時呼叫method1,method3,則不會引互斥,因為鎖住的物件不同。修飾靜態方法時鎖住的是該方法所屬類的Class物件,而修飾成員方法時則是對應的例項物件。

4、

synchronized(Test.class){ //TODO}
同3

5、

synchronized(o) {}
這裡面的o可以是一個任何Object物件或陣列,並不一定是它本身物件或者類,誰擁有o這個鎖,誰就能夠操作該塊程式程式碼。


補充:

執行緒執行互斥程式碼的過程:(所有執行緒共享主記憶體;每個執行緒有自己的工作記憶體)
1、獲取互斥鎖
2、情況工作記憶體
3、從主記憶體中拷貝變數的最新值到工作記憶體
4、執行鎖內部的程式碼
5、將更改後的共享變數的值重新整理到主記憶體
6、釋放互斥鎖


參考文章:
【1】 深入理解Java併發之synchronized實現原理
【2】
https://lrh1993.gitbooks.io/android_interview_guide/content/java/concurrence/synchronized-reentrantlock.html
【3】java synchronized 詳解
【4】java執行緒記憶體模型,執行緒、工作記憶體、主記憶體

相關文章