1. 程式人生 > 其它 >Java內建鎖synchronized

Java內建鎖synchronized

Java內建鎖synchronized的膨脹升級機制,鎖的粗化和消除

Java中鎖是比較難的一塊,包括synchronized,如加鎖之後物件頭資訊的變化是怎樣的?更不用說AQS。不要一開始就糾結共享鎖/獨佔鎖,公平鎖/非公平鎖,以及偏向等,應該從原理層面去理解,一勞永逸。

Synchronized

在JDK1.6之前,synchronized重度依賴於作業系統,涉及到CPU狀態切換,效率很低。(據說Doug Lea因此才開發了AQS框架)。JDK1.6才有了較大的優化,引入了鎖的膨脹升級機制。

檢視物件頭資訊的工具:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.16</version>
</dependency>

32位虛擬機器物件頭組成參考:

synchronized的用法

  1. 修飾指定物件
// 鎖住的只是obj這個物件
Object obj = new Object();

synchronized (obj) {
  System.out.println("...");

  // synchronized是可重入的, 這裡實際上會被優化掉
  synchronized (obj) {
    System.out.println("***");
  }
}
  1. 修飾普通方法
// 鎖住的是具體的例項
public class Test {
  public synchronized void print() {
    System.out.println("...");
  }
}
  1. 修飾靜態方法
// 鎖住的是當前類,靜態成員屬於類
public class Test {
  public static synchronized void print() {
    System.out.println("...");
  }
}

synchronized的升級過程

  • 無鎖:程式執行即刻建立的物件(4s內)
  • 偏向鎖:預設情況,在程式啟動4s後建立的物件,JVM會自動為這些物件加偏向鎖。延遲4s是為了避開程式啟動過程中的資源競爭。
  • 輕量級鎖:多執行緒競爭不激烈
  • 重量級鎖:多執行緒競爭資源激烈

鎖的升級過程是不可逆的。偏向鎖不是真正加鎖,偏向鎖和無鎖狀態是可以轉換的。另外,呼叫hashCode()

會導致鎖升級,無法重新進入偏向狀態,涉及物件頭的結構,很複雜。

synchronized的粗化

Object obj = new Object();

synchronized (obj) {
  synchronized (obj) {
    System.out.println("***");
  }
}

這樣加鎖程式碼可以執行,但實際上裡層的synchronized是完全不必要的,只需留下外層的synchronized即可,而JVM就會做這種優化,即鎖的粗化。

synchronized的消除

Object obj = new Object();

synchronized (obj) {
  synchronized (obj) {
    System.out.println("***");
  }
}

如果本身程式就是單執行緒的,不需要鎖,JVM也會優化,將鎖去掉,即鎖的消除。實際上粗話就消除了裡層的鎖。

由上述內容可以得知,synchronized是可重入鎖。另外,多個執行緒來synchronized(obj)的時候,synchronized無法對執行緒進行排隊,而是執行緒去搶,所以是非公平的。