天天看點

學習——單例模式(Singleton Pattern)

單例模式(Singleton Pattern)

1. 單例模式的定義

單例模式(Singleton Pattern)定義如下:

Ensure a class has only one instance, and provide a global point of access to it.(確定某一個類隻有一個執行個體,而且自行執行個體化并向整個系統提供這個執行個體。)

2.單例模式的應用

2.1 示例代碼(均為線程安全的實作方式)

餓漢單例模式
public class Singleton {
	// 建立一個執行個體
	private static final Singleton singleton = new Singleton();

	private Singleton() {

	}

	/**
	 * 擷取執行個體
	 * 
	 * @return
	 */
	public static Singleton getInstance() {
		return singleton;
	}

	/**
	 * 具體業務邏輯
	 */
	public void method() {

	}
}
           
懶漢單例模式
public class Singleton {
	private static volatile Singleton singleton = null;
	private Singleton() {

	}
	/**
	 * 擷取執行個體
	 * 
	 * @return
	 */
	public static Singleton getInstance() {
		if (singleton == null) {
			synchronized (Singleton.class) {
				if (singleton == null) {
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}

	/**
	 * 具體業務邏輯
	 */
	public void method() {

	}
}
           
内部類單例模式
public class Singleton {
	private static class SingletonHolder {
		//靜态初始化器機制初始化本資料(保證了同步控制,線程安全)
		private static Singleton instance = new Singleton();
}
	private Singleton() {

	}

	/**
	 * 擷取執行個體
	 * 
	 * @return
	 */
	public static Singleton getInstance() {
		return SingletonHolder.instance;
	}

	/**
	 * 具體業務邏輯
	 */
	public void method() {

	}
}
           
枚舉方式單例模式
public enum Singleton {
    SINGLETON;     //執行個體
    /**
	 * 具體業務邏輯
	 */
    public void method(){
    }
    /**
	 * 擷取執行個體
	 * 
	 * @return
	 */
	public static Singleton getInstance() {
		return Singleton.SINGLETON;
	}
}
           

Singleton類稱為單例類,通過使用private的構造函數確定了在一個應用中隻産生一個執行個體,并且是自行執行個體化的(在Singleton中自己使用new Singleton())。

2.2 優點

  • 由于單例模式在記憶體中隻有一個執行個體,減少了記憶體開支,特别是一個對象需要頻繁地建立、銷毀時,而且建立或銷毀時性能又無法優化,單例模式的優勢就非常明顯。
  • 由于單例模式隻生成一個執行個體,是以減少了系統的性能開銷,當一個對象的産生需要比較多的資源時,如讀取配置、産生其他依賴對象時,則可以通過在應用啟動時直接産生一個單例對象,然後用永久駐留記憶體的方式來解決(在Java EE中采用單例模式時需要注意JVM垃圾回收機制)。
  • 單例模式可以避免對資源的多重占用,例如一個寫檔案動作,由于隻有一個執行個體存在記憶體中,避免對同一個資源檔案的同時寫操作。
  • 單例模式可以在系統設定全局的通路點,優化和共享資源通路,例如可以設計一個單例類,負責所有資料表的映射處理。

2.3 缺點

  • 單例模式一般沒有接口,擴充很困難,若要擴充,除了修改代碼基本上沒有第二種途徑可以實作。單例模式為什麼不能增加接口呢?因為接口對單例模式是沒有任何意義的,它要求“自行執行個體化”,并且提供單一執行個體、接口或抽象類是不可能被執行個體化的。當然,在特殊情況下,單例模式可以實作接口、被繼承等,需要在系統開發中根據環境判斷。
  • 單例模式對測試是不利的。在并行開發環境中,如果單例模式沒有完成,是不能進行測試的,沒有接口也不能使用mock的方式虛拟一個對象。
  • 單例模式與單一職責原則有沖突。一個類應該隻實作一個邏輯,而不關心它是否是單例的,是不是要單例取決于環境,單例模式把“要單例”和業務邏輯融合在一個類中。

2.4 使用場景

在一個系統中,要求一個類有且僅有一個對象,如果出現多個對象就會出現“不良反應”,可以采用單例模式,具體的場景如下:

  • 要求生成唯一序列号的環境;
  • 在整個項目中需要一個共享通路點或共享資料,例如一個Web頁面上的計數器,可以不用把每次重新整理都記錄到資料庫中,使用單例模式保持計數器的值,并確定是線程安全的;
  • 建立一個對象需要消耗的資源過多,如要通路IO和資料庫等資源;
  • 需要定義大量的靜态常量和靜态方法(如工具類)的環境,可以采用單例模式(當然,也可以直接聲明為static的方式)。

2.5 注意事項

首先,在高并發情況下,請注意單例模式的線程同步問題。其次,需要考慮對象的複制情況。在Java中,對象預設是不可以被複制的,若實作了Cloneable接口,并實作了clone方法,則可以直接通過對象複制方式建立一個新對象,對象

複制是不用調用類的構造函數,是以即使是私有的構造函數,對象仍然可以被複制。在一般情況下,類複制的情況不需要考慮,很少會出現一個單例類會主動要求被複制的情況,解決該問題的最好方法就是單例類不要實作Cloneable接口。

繼續閱讀