本部落客要是對Java并發中synchronized關鍵字進行較為深入的分析,這些知識點結合部落客對synchronized的個人了解以及相關的書籍的講解(在結尾參考資料),如有誤處,歡迎留言。
1.synchronized的三種應用方式
Java中每一個對象都可以作為鎖,這是synchronized實作同步的基礎:
- 普通同步方法(執行個體方法),鎖是目前執行個體對象 ,進入同步代碼前要獲得目前執行個體的鎖
- 靜态同步方法,鎖是目前類的class對象 ,進入同步代碼前要獲得目前類對象的鎖
- 同步方法塊, 鎖是括号裡面的對象,對給定對象加鎖,進入同步代碼庫前要獲得給定對象的鎖。
具體例子請參考:synchronized的三種應用方式執行個體
1.1總結
synchronized可以用在方法上也可以使用在代碼塊中,其中方法是執行個體方法和靜态方法分别鎖的是該類的執行個體對象和該類的對象。而使用在代碼塊中也可以分為三種,具體的可以看上面的表格。這裡的需要注意的是:如果鎖的是類對象的話,盡管new多個執行個體對象,但他們仍然是屬于同一個類依然會被鎖住,即線程之間保證同步關系。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxieVpXTzsmaOJTT6hVMSdVYopkMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL3MTM1ADMwEjM3ETOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
2.synchronized底層語義原理
Java 虛拟機中的同步(Synchronization)基于進入和退出管程(Monitor)對象實作, 無論是顯式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代碼塊)還是隐式同步都是如此。在 Java 語言中,同步用的最多的地方可能是被 synchronized 修飾的同步方法。同步方法 并不是由 monitorenter 和 monitorexit 指令來實作同步的,而是由方法調用指令讀取運作時常量池中方法的 ACC_SYNCHRONIZED 标志來隐式實作的。
Demo分析
public class SynchronizedDemo {
public static void main(String[] args) {
synchronized (SynchronizedDemo.class) {
}
method();
}
private static void method() {
}
}
上面的代碼中有一個同步代碼塊,鎖住的是類對象,并且還有一個同步靜态方法,鎖住的依然是該類的類對象。編譯之後,切換到SynchronizedDemo.class的同級目錄之後,然後用 javap -v SynchronizedDemo.class檢視位元組碼檔案:
執行同步代碼塊後首先要先執行monitorenter指令,退出的時候monitorexit指令。通過分析之後可以看出,使用Synchronized進行同步,其關鍵就是必須要對對象的螢幕monitor進行擷取,當線程擷取monitor後才能繼續往下執行,否則就隻能等待。而這個擷取的過程是互斥的,即同一時刻隻有一個線程能夠擷取到monitor。上面的demo中在執行完同步代碼塊之後緊接着再會去執行一個靜态同步方法,而這個方法鎖的對象依然就這個類對象,那麼這個正在執行的線程還需要擷取該鎖嗎?答案是不必的,從上圖中就可以看出來,執行靜态同步方法的時候就隻有一條monitorexit指令,并沒有monitorenter擷取鎖的指令。這就是鎖的重入性,即在同一鎖程中,線程不需要再次擷取同一把鎖。Synchronized先天具有重入性。每個對象擁有一個計數器,當線程擷取該對象鎖後,計數器就會加一,釋放鎖後就會将計數器減一。
參考資料:
https://blog.csdn.net/javazejian/article/details/72828483