1.應用場景:
當需要保證類在內存中的對象唯一性,可以使用單例模式,
不想創建多個實例浪費資源,或者避免多個實例由于多次調用
而出現錯誤。一般寫工具類,線程池,緩存,數據庫等可以用到。
2.各種各樣的單例寫法
1.餓漢模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
獲取單例對象:Singleton instace = Singleton.getInstance();
優點:類加載的時候就完成實例化,避免線程同步問題
缺點:由于在類加載的時候就進行了實例化,所以沒有達到Lazy Loading(懶加載)
的效果,即使我們沒用到這個實例,但是他還是會加載,從而造成內存浪費(可以忽略,
最常用的一種)。
2.懶漢模式(線程不安全)
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
盡管達到了懶加載,但是卻存在線程安全問題,比如有兩個線程,剛好都
執行完if(instance == null),接著準備執行instance = new Singleton()
語句,這樣的結果會導致我們實例化了兩個Singleton對象,這就是懶漢單例
模式可能會引發的線程安全問題,解決這個方法,我們可以對getInstance方法加鎖。
3.懶漢模式(線程安全,但效率低)
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
盡管保證了線程安全,但是每個線程在想要獲得實例時,執行getInstance()
方法都需要進行同步,而實例化代碼只需執行一次就夠了,后面獲取該實例,
直接return即可,方法進行同步效率太低,需要改進。還有一種寫法是:
synchronized (Singleton.class) { instance = new Singleton(); }
這樣一樣是線程不安全的,如果你想使用懶漢模式的話,推薦使用下面的:
DCL單例(雙重檢查鎖定)。
4.懶漢模式雙重校驗鎖
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(null == instance){
synchronized (Singleton.class) {
if(null == instance){
instance = new Singleton();
}
}
}
return instance;
}
}
代碼中進行了兩次if檢查,這樣就可以保證線程安全,初始化一次后,
后面再次訪問時,if檢查,直接return 實例化對象。volatile是1.5后
引入的,volatile關鍵字會屏蔽Java虛擬機所做的一些代碼優化,會導
致系統運行效率降低,而更好的寫法是使用靜態內部類來實現單例!
5.靜態內部類實現單例模式
public class Singleton {
private Singleton() {
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
和餓漢式類似,兩者都是通過類裝載機制來保證初始化實例
的時候只有一個線程,從而避免線程安全問題,餓漢式的
Singleton類被加載,就會實例化,而靜態內部類這種,
當Singleton類被加載時,不會立即實例化,調用getInstance方法,
才會裝載SingletonHolder類,從而完成Singleton的實例化。
6.枚舉實現單例模式
public enum SingletonEnum {
INSTANCE;
private Singleton instance;
SingletonEnum() {
instance = new Singleton()
}
public Singleton getInstance() {
return instance;
}
}
訪問方式:SingletonEnum.INSTANCE.method();
INSTANCE即為SingletonEnum類型的引用,得到它就可以調用
枚舉中的方法。既避免了線程安全問題,還能防止反序列化
重新創建新的對象,但是失去了類的一些特性,沒有延時加載,
推薦使用。
7.使用容器實現單例模式
public class SingletonManager {
private static Map<String,Object> objMap = new HashMap<String,Object>();
private Singleton() { }
public static void registerService(String key,Object instance) {
if(!objMap.containsKey(key)) {
objMap.put(key,instance);
}
}
public static Object getService(String key) {
return objMap.get(key);
}
}
將多種單例類型注入到一個統一的管理類中,在使用時根據key獲取對象
對應類型的對象。這種方式使得我們可以管理多種類型的單例,并且在使
用時可以通過統一的接口進行獲取操作,降低了用戶的使用成本,也對用
戶隱藏了具體實現,降低了耦合度。