Java synchronized物件級別與類級別的同步鎖

字母哥部落格發表於2021-06-22

Java synchronized 關鍵字 可以將一個程式碼塊或一個方法標記為同步程式碼塊。同步程式碼塊是指同一時間只能有一個執行緒執行的程式碼,並且執行該程式碼的執行緒持有同步鎖。synchronized關鍵字可以作用於

  • 一個程式碼塊
  • 一種方法

當一個方法或程式碼塊被宣告為synchronized時,如果一個執行緒正在執行該synchronized 方法或程式碼塊,其他執行緒會被阻塞,直到持有同步鎖的執行緒釋放。根據鎖定的範圍可以分為

  • 類級別的鎖可以防止多個執行緒在執行時同時進入該類所有例項化物件的 synchronized程式碼塊中。
  • 物件級別的鎖可以防止多個執行緒在執行時同時進入當前(或某一個)例項化物件的 synchronized程式碼塊中。

1. 物件級別的同步鎖

物件級別的同步鎖:當我們想要在多執行緒環境下同步執行一個非靜態方法或非靜態程式碼塊時,在類的方法或程式碼塊加上synchronized關鍵字,可以保證物件例項級別資料的執行緒安全。(比較後文的類級別的同步鎖,回頭來理解這句話)

物件級別的加鎖的程式碼如下,如:在方法上加鎖,鎖物件為當前類的例項化物件

public class DemoClass{
	public synchronized void demoMethod(){}
}

如:為程式碼塊加鎖,鎖物件為this物件

public class DemoClass{
	public void demoMethod(){
		synchronized (this){
			//同步程式碼塊
		}
	}
}

如:為程式碼塊加鎖,鎖物件為我們建立的任意一個物件。不要使用非final的成員變數作為同步鎖物件,因為非final成員變數可以被重新賦值,導致不同的執行緒使用不同的物件作為鎖,達不到同步鎖定的效果。

public class DemoClass{
    //注意這裡的關鍵字final非常重要,看說明
	private final Object lock = new Object();
	public void demoMethod(){
		synchronized (lock){
			//同步程式碼塊
		}
	}
}

2. 類級別的同步鎖

類級別的鎖可以防止多個執行緒在執行時進入該類所有例項化物件的 "synchronized塊中。也就是說如果執行時有100個DemoClass的例項,那麼每次只有一個執行緒能夠在任何一個例項中執行demoMethod(),所有其他例項的所有其他執行緒都被鎖定。

為了保障靜態資料執行緒安全,應該使用類級別的鎖定。我們知道static關鍵字將方法的資料關聯到類的級別上,所以在靜態方法上使用鎖。

靜態方法加鎖,對該類所有的例項化物件生效

public class DemoClass{
	//靜態方法加鎖,對該類所有的例項化物件生效
	public synchronized static void demoMethod(){

	}
}

獲取 .class類的引用,類級別的鎖

public class DemoClass{
	public void demoMethod(){
		//獲取 .class類的引用,類級別的鎖,對該類所有的例項化物件生效
		synchronized (DemoClass.class){
			//同步程式碼塊
		}
	}
}

使用靜態物件的鎖,類級別的鎖

public class DemoClass{
    //靜態物件,類級別,注意這裡的關鍵字final非常重要
	private final static Object lock = new Object();
	public void demoMethod(){
		//使用靜態物件的鎖,類級別鎖,對該類所有的例項化物件生效
		synchronized (lock){
			//同步程式碼塊
		}
	}
}

3. 總結

  1. Java中的同步機制保證了兩個或多個執行緒無法同時執行一個需要相同同步鎖的方法。
  2. "synchronized "關鍵字只能用於方法和程式碼塊。這些方法或程式碼塊可以是靜態非靜態的。
  3. 當一個執行緒進入synchronized方法或程式碼塊時,它就會獲得一個鎖,當它離開同步方法或程式碼塊時,它就會釋放這個鎖。如果執行緒執行過程出現任何錯誤或異常,鎖也會被釋放。
  4. 使用"synchronized "關鍵字持有的鎖在本質上是可重入的,這意味著如果一個同步方法呼叫另一個使用相同鎖的同步方法,那麼持有鎖的當前執行緒可以進入該方法而無需再次獲得鎖。
  5. 如果同步塊中使用的物件為空,Java synchronized 將丟擲NullPointerException
  6. 使用synchronized同步方法會給你的應用程式帶來效能成本。因此,儘量在絕對需要的情況下才使用同步。另外優先考慮使用同步程式碼塊,並且只同步程式碼的關鍵部分。
  7. 靜態同步方法和非靜態同步方法有可能同時或併發執行,因為它們使用的是不同的鎖。
  8. 根據Java語言規範,你不能在建構函式中使用synchronized關鍵字。這是不合法的,會導致編譯錯誤。
  9. 不要使用非final的成員變數作為同步鎖物件,因為非final成員變數可以被重新賦值,導致不同的執行緒使用不同的物件作為鎖,達不到同步鎖定的效果。
  10. 不要使用字串字面量作為鎖物件,如:String a = "1";,因為它們可能會被應用程式中的其他地方引用,並可能導致死鎖。用new關鍵字建立的字串物件可以安全使用。

歡迎關注我的部落格,裡面有很多精品合集

  • 本文轉載註明出處(必須帶連線,不能只轉文字):字母哥部落格

覺得對您有幫助的話,幫我點贊、分享!您的支援是我不竭的創作動力! 。另外,筆者最近一段時間輸出瞭如下的精品內容,期待您的關注。

相關文章