Head First 設計模式之單例模式(Java例子)

前言:

來源於《head first 設計模式》。當作讀書筆記了,這次看的是第五章裝單例模式。連續看了兩章,下一章預告命令模式。單例模式看着簡單,但其中的道理並不少。

單例模式的概念

用於確保一個類只有一個實例,並提供全局訪問點。

由於過於簡單吧,跳過日常舉例子的環節,直接寫出一步步演變的單例模式

經典單例模式

public class Singleton {
	private static Singleton uniqueInstance;
 	//私有化構造方法
	private Singleton() {}
 	//提供全局訪問點
	public static Singleton getInstance() {
		if (uniqueInstance == null) {
			uniqueInstance = new Singleton();
		}
		return uniqueInstance;
	}
 
	// other useful methods here
	public String getDescription() {
		return "I'm a classic Singleton!";
	}

上述實現方法屬於延遲實例化(lazy instantiaze),利用private讓構造方法私有化,static修飾getInstance提供全局訪問。
不過把這個簡單單例模式存在一定的弊端哦,就是在多線程模式的時候,會產生不同的實例變量。
例如:有兩個線程。線程一執行getInstance進入到判斷uniqueInstance是否爲null,爲null準備實例化,此時跳轉到線程二執行,線程二判斷uniqueInstance==null,也是爲null,也進入了實例化過程。=。=這就gg公司了啊,線程二創建完實例化對象後,執行其他方法,而到線程一的時候又創建實例化對象覆蓋,執行其他方法。此時可能會把單例模式對象的一些初始化數據又出初始化了。
那麼只能引入同步咯或者先初始化

解決同步的單例模式

  • 先初始化
public class Singleton {
	private static Singleton uniqueInstance = new Singleton();
 
	private Singleton() {}
 
	public static Singleton getInstance() {
		return uniqueInstance;
	}
	
	// other useful methods here
	public String getDescription() {
		return "I'm a statically initialized Singleton!";
	}
}

直接在成員變量處讓其初始化。。即類加載器加載此類的時候順路初始化。這樣就不會存在併發問題咯。

  • 引入同步
public class Singleton {
	private static Singleton uniqueInstance;
 
	// other useful instance variables here
 
	private Singleton() {}
 
	public static synchronized Singleton getInstance() {
		if (uniqueInstance == null) {
			uniqueInstance = new Singleton();
		}
		return uniqueInstance;
	}
 
	// other useful methods here
	public String getDescription() {
		return "I'm a thread safe Singleton!";
	}
}

其實就是加入synchronized關鍵字,迫使每次只能一個線程訪問此方法。但同時也帶來了另外一個問題。降低了性能。我們不難發現一個問題,多線程併發問題只存在於對象的初始化階段,往後的每次調用不需要同步啊。直接獲取已實例化的對象。這樣子的話同步貌似就成爲了一種累贅。
那麼如何改進呢?

public class Singleton {
	private volatile static Singleton uniqueInstance;
 
	private Singleton() {}
 
	public static Singleton getInstance() {
		if (uniqueInstance == null) {
			synchronized (Singleton.class) {
				if (uniqueInstance == null) {
					uniqueInstance = new Singleton();
				}
			}
		}
		return uniqueInstance;
	}
}

用volatile關鍵字,使uniqueInstance變量修改的時候每個線程都能夠知道,不會出現髒讀的現象。
判斷爲null後再進行同步。只有第一次初始化纔會徹底執行完代碼。初始化完成後,不會再進行同步的判斷。

總結

單例模式的主要實現步驟就是1.私有化構造方法 2.提供訪問點,加上注意多線程併發問題即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章