Java內建鎖synchronized
阿新 • • 發佈:2021-09-05
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的用法
- 修飾指定物件
// 鎖住的只是obj這個物件
Object obj = new Object();
synchronized (obj) {
System.out.println("...");
// synchronized是可重入的, 這裡實際上會被優化掉
synchronized (obj) {
System.out.println("***");
}
}
- 修飾普通方法
// 鎖住的是具體的例項 public class Test { public synchronized void print() { System.out.println("..."); } }
- 修飾靜態方法
// 鎖住的是當前類,靜態成員屬於類
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
無法對執行緒進行排隊,而是執行緒去搶,所以是非公平的。