單例模式

單例模式,顧名思義就是在內存中只有一個類的對象實例,對於要佔用重要系統資源的對象,我們常採用單例模式,比如Web中的Servlet,Hibernate中的sessionFectory等都是採用的單例模式。

單例模式有多重實現方式,但這些實現方式中都有一下共同點:

  1. 有一個私有的無參構造函數,這可以防止其他類實例化它,而且單例類也不應該被繼承,如果單例類允許繼承那麼每個子類都可以創建實例,這就違背了Singleton模式“唯一實例”的初衷。
  2. 單例類被定義爲sealed,就像前面提到的該類不應該被繼承,所以爲了保險起見可以把該類定義成不允許派生,但沒有要求一定要這樣定義。
  3. 一個靜態的變量用來保存單實例的引用。
  4. 一個公有的靜態方法用來獲取單實例的引用,如果實例爲null即創建一個。

單例模式的幾種實現方式:

 

 方式一:餓漢式單例

 

public class Singleton {
	private static Singleton singleton = new Singleton();
	private Singleton(){}
	public static Singleton getInstance(){
		return singleton;
	}
}
這種方式避免了多線程的同步問題,不過,instance在類裝載時就實例化,沒有達到lazy loading的效果。
 
方式二: 懶漢式單例
 
public class Singleton {  
    private static Singleton singleton;  
    private Singleton(){}  
      
    public static Singleton getInstance(){  
        if(singleton==null){  
            singleton = new Singleton();  
        }  
        return singleton;  
    }  
}  
<span style="font-size:18px;"></span>

 

這是一種非線程安全的單例模式,可進行如下修改使其線程安全

 

public class Singleton {  
    private static Singleton singleton;  
    private Singleton(){}  
      
    public static synchronized Singleton getInstance(){  
        if(singleton==null){  
            singleton = new Singleton();  
        }  
        return singleton;  
    }  
}  
<span style="font-size:18px;"><strong></strong></span> 

 

這種寫法能夠在多線程中很好的工作,而且看起來它也具備很好的lazy loading,但是,遺憾的是,效率很低,99%情況下不需要同步。

 

方法三:靜態內部類

public class Singleton {   
    private static class SingletonHolder {   
    private static final Singleton INSTANCE = new Singleton();   
    }   
    private Singleton (){}   
    public static final Singleton getInstance() {   
    return SingletonHolder.INSTANCE;   
    }   
}  
<span style="font-size:18px;"><span></span></span>

 

 

    這種方式同樣保證初始化instance時只有一個線程,它跟方式一的不同在於:方式一是隻要Singleton類被裝載了,那麼instance就會被實例化(沒有達到lazy loading效果),而這種方式是Singleton類被裝載了,instance不一定被初始化。因爲SingletonHolder類沒有被主動使用,只有顯示通過調用getInstance方法時,纔會顯示裝載SingletonHolder類,從而實例化instance。想象一下,如果實例化instance很消耗資源,我想讓他延遲加載,另外一方面,我不希望在Singleton類加載時就實例化,因爲我不能確保Singleton類還可能在其他的地方被主動使用從而被加載,那麼這個時候實例化instance顯然是不合適的。這個時候,這種方式相比方式一就顯得很合理。

單例模式的優點

  • 在內存中只有一個對象,節省內存空間。
  • 避免頻繁的創建銷燬對象,可以提高性能。
  • 避免對共享資源的多重佔用。
  • 可以全局訪問。

適用場景:由於單例模式的以上優點,所以是編程中用的比較多的一種設計模式。我總結了一下我所知道的適合使用單例模式的場景:

  • 需要頻繁實例化然後銷燬的對象。
  • 創建對象時耗時過多或者耗資源過多,但又經常用到的對象。
  • 有狀態的工具類對象。
  • 頻繁訪問數據庫或文件的對象。
  • 以及其他我沒用過的所有要求只有一個對象的場景。

單例模式注意事項:

  • 只能使用單例類提供的方法得到單例對象,不要使用反射,否則將會實例化一個新對象。
  • 不要做斷開單例類對象與類中靜態引用的危險操作。
  • 多線程使用單例使用共享資源時,注意線程安全問題。

在一個jvm中會出現多個單例嗎

        在分佈式系統、多個類加載器、以及序列化的的情況下,會產生多個單例,這一點是無庸置疑的。那麼在同一個jvm中,會不會產生單例呢?使用單例提供的getInstance()方法只能得到同一個單例,除非是使用反射方式,將會得到新的單例。代碼如下

 

Class c = Class.forName(Singleton.class.getName());  
Constructor ct = c.getDeclaredConstructor();  
ct.setAccessible(true);  
Singleton singleton = (Singleton)ct.newInstance(); 

   

 

 這樣,每次運行都會產生新的單例對象。所以運用單例模式時,一定注意不要使用反射產生新的單例對象。以上也是反射機制的一個缺點,反射的權限太高了,它甚至可以訪問類中的私有方法,這無疑會破壞類的封裝性。

 

 

 

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