一、定義
單例模式(Singleton Pattern):單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例,這個類稱爲單例類,它提供全局訪問的方法。
二、基本結構
一個私有Singleton屬性
private static Singleton instance = null;
一個構造方法
private Singleton(){} // 確保外部不可見且自身調用
一個用來外部調用的公有方法,並在此方法中進行創建對象
public static Singleton getInstance(){
if(instance == null){ // 確保這個類只有一個實例
instance = new Singleton();
}
return instance;
}
三、時序圖
四、代碼分析
1、懶漢式,線程不安全
描述: 這種方式是最基本的實現方式,這種實現最大的問題就是不支持多線程。因爲沒有加鎖 synchronized,所以嚴格意義上它並不算單例模式。
public Singleton {
private static instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
2、懶漢式,線程安全
描述:這種方式能夠在多線程中很好的工作,但是,效率很低,99% 情況下不需要同步。
優點:第一次調用才初始化,避免內存浪費。
缺點:必須加鎖 synchronized 才能保證單例,但加鎖會影響效率。
public Singleton {
private static instance = null;
private Singleton() {
}
public static Singleton synchronized getInstance() {
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
3、餓漢式
描述:這種方式比較常用,但容易產生垃圾對象。它基於 classloder 機制避免了多線程的同步問題,不過instance 在類裝載時就實例化,雖然導致類裝載的原因有很多種,在單例模式中大多數都是調用 getInstance 方法, 但是也不能確定有其他的方式導致類裝載,這時候初始化 instance 顯然沒有達到 lazy loading 的效果。
優點:沒有加鎖,執行效率會提高。
缺點:類加載時就初始化,浪費內存。
public Singleton {
private static instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return new Singleton();
}
}
4、雙檢鎖/雙重校驗鎖
這種方式採用雙鎖機制,安全且在多線程情況下能保持高性能。getInstance() 的性能對應用程序很關鍵。
public Singleton {
private volatile static instance = null; // volatile 保證對象的原子性
private Singleton() {
}
public static Singleton getInstance() {
if(instance == null){
synchronized(Signleton.class) {
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
5、靜態內部類
這種方式能達到雙檢鎖方式一樣的功效,但實現更簡單。對靜態域使用延遲初始化,應使用這種方式而不是雙檢鎖方式。這種方式只適用於靜態域的情況,雙檢鎖方式可在實例域需要延遲初始化時使用。
這種方式同樣利用了 classloder 機制來保證初始化 instance 時只有一個線程,它跟第 3 種方式不同的是:第 3 種方式只要 Singleton 類被裝載了,那麼 instance 就會被實例化(沒有達到 lazy loading 效果),而這種方式是 Singleton 類被裝載了,instance 不一定被初始化。因爲 SingletonHolder 類沒有被主動使用,只有通過顯式調用 getInstance 方法時,纔會顯式裝載 SingletonHolder 類,從而實例化 instance。想象一下,如果實例化 instance 很消耗資源,所以想讓它延遲加載,另外一方面,又不希望在 Singleton 類加載時就實例化,因爲不能確保 Singleton 類還可能在其他的地方被主動使用從而被加載,那麼這個時候實例化 instance 顯然是不合適的。這個時候,這種方式相比第 3 種方式就顯得很合理。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
五、優缺點
優點:
1、提供了對唯一實例的受控訪問。因爲單例類封裝了它的唯一實例,所以它可以嚴格控制客戶怎樣以及何時訪問它,併爲設計及開發團隊提供了共享的概念。
2、由於在系統內存中只存在一個對象,因此可以節約系統資源,對於一些需要頻繁創建和銷燬的對象,單例模式無疑可以提高 系統的性能。
3、允許可變數目的實例。我們可以基於單例模式進行擴展,使用與單例控制相似的方法來獲得指定個數的對象實例。
缺點:
1、由於單例模式中沒有抽象層,因此單例類的擴展有很大的困難。
2、單例類的職責過重,在一定程度上違背了“單一職責原則”。因爲單例類既充當了工廠角色,提供了工廠方法,同時又充當了產品角色,包含一些業務方法,將產品的創建和產品的本身的功能融合到一起
3、濫用單例將帶來一些負面問題,如爲了節省資源將數據庫連接池對象設計爲單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;現在很多面嚮對象語言(如Java、C#)的運行環境都提供了自動垃圾回收的技術,因此,如果實例化的對象長時間不被利用,系統會認爲它是垃圾,會自動銷燬並回收資源,下次利用時又將重新實例化,這將導致對象狀態的丟失。