1. 程式人生 > >設計模式【單例模式】

設計模式【單例模式】

imp 壓力 多個實例 代碼 建模 否則 參與 creates 沒有

單例設計模式是設計模式中使用最為普通的模式之一。它是一種對象創建模式,用於產生一個對象的實例,它可以確保系統中一個類只產生一個實例。在Java語言中,這樣的行為能帶來兩大好處:

  (1)對於頻繁使用的對象,可以省略創建對象所花費的時間,這對於那些重量級對象而言,是非常可觀的一筆系統開銷。

  (2)由於new操作的次數越少,因而對系統內存的使用頻率也會降低,這將減輕GC壓力,縮短GC停頓時間。

因此對於系統的關鍵組件和被頻繁使用的對象,使用單例模式可以有效地改善系統的性能。

單例模式的參與者和參與者非常簡單,只有單例類和使用者兩個,如下圖:

技術分享

  單例模式的核心在於通過一個接口返回唯一的對象實力。一個簡單的單例實現

public class Singleton {
	private Singleton(){
		System.out.println("Singleton is create.");//創建單例的過程可能會比較慢
	}
	private static Singleton instance = new Singleton();
	public static Singleton getInstance(){
		return instance;
	}
}

  註意代碼中標註紅色的部分,首先單例類必須要有一個private訪問級別的構造函數,只有這樣,才能確保單例不會在系統中的其他代碼內被實例化,這是相當重要的

;其次instance成員變量和getInstance()方法必須是static的。

  這種單例的實現方式很簡單,而且十分可靠。他唯一不足僅是無法對instance實例做延遲加載。假如單例的創建過程很緩慢,而由於instance成員變量是static定義的,因此在JVM加載單立時,單例對象就會被建立,如果此時這個單例在系統中漢扮演其他角色,那麽在任何使用這個單例的地方都會初始化這個單例變量,而不管是否會被用到。比如單例類作為String工廠,用於創建一些字符串(該類及用於創建單例Singleton,又用於創建String對象):

public class Singleton {
	private Singleton(){
		System.out.println("Singleton is create.");//創建單例的過程可能會比較慢
	}
	private static Singleton instance = new Singleton();
	public static Singleton getInstance(){
		return instance;
	}
	public static void createString(){//這是單例扮演其他角色
		System.out.println("createString in Singleton.");
	}
}

  當時用Singleton. createString()執行任務時,程序輸出如下圖:

技術分享

  從輸出結果可以看到,雖然此時並沒用使用單例類,但它還是被創建出來,這也許是開發人員不願意看到的。為了解決這個問題,並以此提高系統在相關函數調用時的反應速度,就需要引入延遲加載機制。

public class LazySingleton {
	private LazySingleton(){
		System.out.println("LazySingleton is create.");//創建單例的過程可能會比較慢
	}
	private static LazySingleton instance = null;
	public static synchronized LazySingleton getInstance(){
		if(instance==null){
			instance = new LazySingleton();
		}
		return instance;
	}
}

  首先,對於靜態成員變量instance初始值賦予null,確保系統啟動時沒有額外的負載;其次,在getInstance()工廠方法中,判斷當前單例是否存在,若存在則返回,不存在則再創建單例。這裏尤其還要註意,getInstance()方法必須是同步的,否則在多線程環境下,當線程1正再創建單例時,完成賦值操作前,線程2可能判斷instance為null,故線程2也將啟動新建單例的程序,而導致多個實例被創建,故同步關鍵字是必須的。

  使用上例中的單例實現,雖然實現了延遲加載的功能,但和第一種方法相比,它引入了同步關鍵字,因此在多線程環境中,它的時耗要遠遠大於大於第一種單例模式。

  為了使用延遲加載引入同步關鍵字反而降低了系統性能,是不是有點得不償失?為了解決這個問題,還需要對其進行改進:

public class StaticSingleton {

	private StaticSingleton(){
		System.out.println("StaticSingleton is create.");
	}
	private static class SingletonHolder{
		private static StaticSingleton instance = new StaticSingleton();
	}
	public static StaticSingleton getInstance(){
		return SingletonHolder.instance;
	}
}

  在這個實現中,單例模式使用內部類來維護單例的實例,當StaticSingleton被加載時,其內部類並不會被初始化,故可以確保當StaticSingleton類被載入JVM時,不會初始化單例類,而當getInstance()方法被調用時,才會加載SingletonHoider,從而初始化instance。同時,由於實例的建立是在類加載時完成,故天生對多線程友好,getInstance()方法也不需要使用同步關鍵字。因此,這種實現方式同時兼備以上兩種實現的優點。

  通常情況下,用以上方式實現的單例已經可以確保在系統中只存在唯一實例了。但仍然有例外情況,可能導致系統生成多個實例,比如在代碼中,通過反射機制,強行調用單列類的私有構造函數,生成多個單例。

  一個可以被串行化的單例:

public class SerSingleton implements Serializable{

	String name;
	
	private SerSingleton(){
		System.out.println("SerSingleton is create.");
		name = "SerSingleton";
	}
	
	private static SerSingleton instance = new SerSingleton();
	
	public static SerSingleton getInstance (){
		return instance;
	}
	
	public static void createString(){
		System.out.println("createString in Singleton.");
	}
	
	private Object readResolve(){
		return instance;
	}
}

  

設計模式【單例模式】