java

單例模式很簡單,但99%的人寫不對。

簡介

引入單例模式

單例模式概念特別簡單,我當初也想當然的覺得程式碼真的顯而易見,當初學的時候連程式碼都看的不仔細,所以很多次和別人交流的時候才發現原來還有很多沒有掌握。很多細節稍不注意就錯了。

將介紹哪些給你

為了節省篇幅,這篇博文不會介紹簡單的單例模式的實現方法,只會介紹雙所檢測版的單例模式的實現方法,並且指出有哪些很容易被忽視和出錯的技術細節。

何為單例模式

通俗的講

就是一個類,在一個應用程式裡,只允許生成一個例項物件。

雙鎖檢測(DLC)版的單例模式

正確的寫法

雙檢測鎖(double check lock)單例模式,通俗來講就是需要進行兩次進行非空檢測,並且需要加鎖進行同步控制,是執行緒安全的單例模式實現方式之一;正確的程式碼書寫方式之一如下,可能讀者會疑惑,為什麼加鎖的程式碼會這麼複雜晦澀,其實他這樣設計是基於效能最佳化考慮的,稍後會分析。

public class DemoClazz{
	private static volatile DemoClazz instance;
	private DemoClazz(){}
	public static DemoClazz getInstance(){
		if(instance == null){
			synchronized(DemoClazz.class){
				if(instance == nul){
					instance = new DemoClazz();
				}
			}
		}
		return instance;
	}
	

}

易錯點

一、構造方法私有化

private DemoClazz(){}

構造方法必須私有化,我們學習java時都只見過public修飾的構造方法,但是構造方法是允許用private修飾的。

構造方法非私有化,會導致程式呼叫者可以私自呼叫構造方法例項化物件,從而破壞單例模式的特性。

二、static關鍵字不能漏

private static volatile DemoClazz instance;

public static DemoClazz getInstance(){}

getInstance()方法用來獲取單例,由於物件在類載入進來時是沒有例項物件存在的,所以只能透過static方法來獲取類例項。

同理static方法中訪問到的屬性均需要為類屬性(static修飾的屬性),所以用來存放例項物件引用的屬性instance用static修飾。

private static volatile DemoClazz instance;

三、instance需要private修飾

instance只有將訪問許可權控制在本類方法中,才能保證邏輯的正確,如果使用者直接獲取instance,可能會獲取到一個null值。

四、instance需要volatile修飾

在 JVM的記憶體模型中,每個執行緒讀取和操作物件的屬性時,並不是直接在記憶體中操作,而是生成一個副本進行存取,volatile關鍵字可以保證現場之間對該變數保持可見性,即執行緒A對變數c的修改,執行緒B在下次讀取變數c時能立馬感知到。

儘管有關鍵字synchronized關鍵字,如果不加上volatile關鍵字,可能會導致在併發場景下執行緒各自生成了例項物件在各自的執行緒工作空間裡。

五、雙檢測鎖例項化的邏輯

	public static DemoClazz getInstance(){
		if(instance == null){
			synchronized(DemoClazz.class){
				if(instance == nul){
					instance = new DemoClazz();
				}
			}
		}
		return instance;
	}

getInstance方法會進行兩次判空操作;第一次,判斷是否例項化了,有例項物件則直接返回,不需要其他操作。如果沒有例項化,則說明是程式第一次去獲取例項對像,會進行一次加鎖操作,只允許一個執行緒進入方法,進入方法之後的判空操作是為了僅允許第一個進入的執行緒進行例項化,其他執行緒不允許例項化。

因為例項化操作僅需要進行一次同步,所以可以用第一次判空操作來進行避免。至於第二次判空操作,則是為了保證單例。

本文章已修改原文用詞符合繁體字使用者習慣使其容易閱讀

版權宣告:此處為CSDN博主「Mr. 良爺」的原創文章,依據CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。

原文連結:https://blog.csdn.net/liangcheng0523/article/details/109114084