單例模式 - Head First設計模式學習

單例模式是比較簡單的設計模式。直接給出定義就明白了。

單例模式確保一個類只有一個實例,並提供一個全局訪問點

要保證一個類只有一個實例需要做到以下幾點:

  1. 私有類構造器,防止別的類直接用類構造器實例化實例
  2. 既然構造器私有了,那麼只有類本身能夠實例化。類需要提供一個公開的接口獲取實例
  3. 類本身需要保證實例的單一

類圖如下:
在這裏插入圖片描述

類怎麼創建單一的實例,有以下幾種辦法:

錯誤的示範

public class Singleton {
	
	private static Singleton instance;
	
	//私有的構造器
	private Singleton() {}
	
	//公開的訪問點
	public static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}

上面的代碼看似符合單例模式,但是類本身無法保證只實例化一次實例。

在多線程的情況下,可能有多個線程得到 instance == null 爲true的結果。

因此單例模式比較重要的就是怎麼避免多線程重複實例化的問題。

使用 synchronized 同步

public class Singleton {
	
	private static Singleton instance;
	
	//私有的構造器
	private Singleton() {}
	
	//公開的訪問點,加上synchronized避免多線程同時進入方法中
	public static synchronized Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
	
}

比較簡單的方法,直接在實例方法上加synchronized,兩個線程無法同時進入方法,自然不會重複實例化了。

不好的是線程的互相等待影響效率。

實例化靜態變量

public class Singleton {
	
	//加載類時就創建對象,由jvm保證單例
	private static Singleton instance = new Singleton();
	
	//私有的構造器
	private Singleton() {}
	
	//公開的訪問點
	public static Singleton getInstance() {
		return instance;
	}
	
}

靜態變量的實例jvm加載類時會自動創建,並且保證實例的唯一。

這個辦法不好的地方是,即使不需要這個實例它也會創建。

雙重檢查加鎖

public class Singleton {
	
	//volatile保證不同線程之間的變量是一樣的
	private volatile static Singleton instance;
	
	//私有的構造器
	private Singleton() {}
	
	//公開的訪問點,沒有實例化的前提下加鎖
	public static Singleton getInstance() {
		if (instance == null) {
			synchronized (Singleton.class) {
				if (instance == null) {
					instance = new Singleton();					
				}
			}
		}
		return instance;
	}
	
}

訪問點在沒有實例化的判斷之後加鎖,各個線程最多隻有第一次獲取實例的時候會被同步,提高了效率。

注意的是實例變量需要設置爲 volatile。保證各個線程看到的全局變量一致。否則第二次的檢查也有可能出現多線程錯誤。

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