單例模式
簡介
單例模式是保證系統實例唯一性的重要手段。單例模式首先通過將類的實例化方法私有化防止程式通過其他方式創建該類的實例,然後通過提供一個全局唯一獲取該類實例的方法幫助用戶獲取該類的實例,用戶只需也只能通過調用該方法獲取類的實例。
單例模式的常見寫法有懶漢式,餓漢式,靜態內部類,雙重校驗鎖等。
具體實現
1.懶漢式(執行緒安全)
懶漢模式很簡單,定義一個私有的靜態對象instance。之所以定義instance為靜態,是因為靜態屬性或方法是屬於類的,能夠很好地保障單例對象的唯一性。然後定義一個加鎖的靜態方法獲取該對象,如果該對象為null,則定義一個對象實例並將其賦值給instance,這樣下次再獲取該對象時便能夠直接獲取了。
因為在獲取對象實例時做了加鎖操作,因此時執行緒安全的。
//懶漢式 public class LazySingleton { private static LazySingleton instance; private LazySingleton(){ } public static synchronized LazySingleton getInstance(){ if(instance == null){ instance = new LazySingleton(); } return instance; } }
2.餓漢式
餓漢模式指在類中直接定義全局的靜態對象的實例並初始化,然後提供一個方法獲取該實例對象。懶漢式和餓漢式最大的不同在於,懶漢式在類中定義了單例但是未實例化,實例化的過程是在獲取單例對象的方法中實現的,也就是說,第一次調用懶漢式時,該對象一定為空,然後去實例化對象並賦值,這樣下次就能直接獲取到對象了。餓漢式在定義單例對象的同時將其實例化了,直接使用即可。也就是說,在餓漢式下,在classloader 完成後該類的實例便已經存在於JVM中了。
//餓漢式 public class HungrySingleton { private static HungrySingleton instance = new HungrySingleton(); private HungrySingleton(){ } public static HungrySingleton getInstance(){ return instance; } }
3.靜態內部類
通過在類中定義一個靜態內部類,將對象實例的定義和初始化放在內部類中完成,我們在獲取對象時要通過靜態內部類調用其單例對象。之所以這樣設計,是因為類的靜態內部類在JVM中是唯一的,這樣很好的保障了單例對象的唯一性。
同樣利用了 classloader 機制來保證初始化 instance 時只有一個執行緒,它跟餓漢式不同的是:餓漢式只要 Singleton 類被裝載了,那麼 instance 就會被實例化(沒有達到 lazy loading 效果),而這種方式是 Singleton 類被裝載了,instance 不一定被初始化。因為 SingletonHolder 類沒有被主動使用,只有通過顯式調用 getInstance 方法時,才會顯式裝載 SingletonHolder 類,從而實例化 instance。
public class Singleton { private static class SingletonHolder{ private static final Singleton INSTANCE = new Singleton(); } private Singleton(){ } public static final Singleton getInstance(){ return SingletonHolder.INSTANCE; } }
4.雙重校驗鎖
該模式是在懶漢模式的基礎上做了進一步優化,給靜態對象的定義加上volatile鎖來保障初始化時對象的唯一性,在獲取對象時通過synchronized(Singleton.class)給單例類加鎖來保障操作的唯一性。
public class Lock2Singleton { private volatile static Lock2Singleton singleton; private Lock2Singleton(){ } public static Lock2Singleton getSingleton() { if (singleton == null) { synchronized (Lock2Singleton.class) { if (singleton == null) { singleton = new Lock2Singleton(); } } } return singleton; } }