設計模式【單例模式】
單例設計模式是設計模式中使用最為普通的模式之一。它是一種對象創建模式,用於產生一個對象的實例,它可以確保系統中一個類只產生一個實例。在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實例做延遲加載。假如單例的創建過程很緩慢,而由於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; } }
設計模式【單例模式】