[設計模式] 創建型:單例模式(Singleton Pattern)

什麼是單例模式

單例就是單個對象的意思,指在系統運行期間,一個類最多隻能創建一個對象,且該類能自行創建這個對象的一種編碼設計模式。

單例模式有兩個特點:

  1. 在系統的整個生命週期內,單例對象最多隻能有一個
  2. 單例對象必須由單例類自行創建,並對外提供訪問入口

一個類可以創建多個對象,這是面向對象的語言特性,想要實現單例模式,就要屏蔽這個特性,防止系統可以隨意創建類的對象。

要做到這一點,通常做法就是利用private關鍵字將類的構造方法私有化,使外部調用者無法利用new關鍵字創建類的對象。一旦私有化了類的構造方法,就意味着能夠使用new關鍵字創建對象的權利只有該類自己擁有!所以該類就必須自己創建單例,並對外界提供可訪問該單例對象的靜態方法。

爲什麼是靜態方法?因爲外界不可能創建單例類的對象,也就沒有可能直接調用對象的方法。再進一步可推測出單例對象也要用static關鍵字修飾,因爲靜態方法中無法訪問對象成員變量。

單例模式的實現

餓漢模式

單例類加載或者系統啓動的時候,就創建好單例對象,不管後面會不會使用。

public class Singleton {
	private static Singleton singleton = new Singleton();

	private Singleton() {
	}

	public static Singleton getInstance() {
		return singleton;
	}
}

餓漢模式不存在線程安全問題,但是也要注意在類加載、系統啓動的時候會不會有其它問題。

懶漢模式

系統啓動運行期間不創建單例對象,只在第一次調用單例類入口方法時纔去創建單例對象。

特點:不使用不創建,使用時才創建,儘可能延遲單例對象的實例化。

具體代碼實現又可分爲兩大應用場景:線程不安全、線程安全。

線程不安全

public class Singleton {
    private static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

線程安全

保證線程安全的第一種方式:簡單粗暴,直接使用synchronized靜態方法。

public class Singleton {
	private static Singleton singleton;

	private Singleton() {
	}

	public static synchronized Singleton getInstance() {
		if (singleton == null) {
			singleton = new Singleton();
		}
		return singleton;
	}
}

保證線程安全的第二種方式:雙重加鎖檢查DCL(Double Check Lock)

public class Singleton {
	// volatile關鍵字必須有
	private volatile static Singleton singleton;

	private Singleton() {
	}

	public static Singleton getInstance() {
		if (singleton == null) {// 第一次校驗
			synchronized (Singleton.class) {// 加鎖
				if (singleton == null) {// 第二次校驗
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}
}

雙重加鎖檢查的要點有兩個:

  1. 單例對象需要使用volatile修飾,保證單例對象完全初始化後才能被訪問到
  2. 加鎖前後,兩次檢查單例對象是否已被創建,防止多次重複創建

保證線程安全的第三種方式:靜態內部類

public class Singleton {
	private static class Holder {
		private static Singleton singleton = new Singleton();
	}

	private Singleton() {
	}

	public static Singleton getInstance() {
		return Holder.singleton;
	}
}

這種方式特點就是不需要加鎖,利用內部類延時加載機制達到單例類的延時創建效果,值得推薦和使用!

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