java設計模式之單例模式

java單例模式

java單例模式是一種常見的設計模式,單例模式分爲3種
  1. 懶漢式單例
  2. 餓漢式單例
  3. 登記式單例

單例模式的特點

  1. 單例類只能有一個實例
  2. 單例類必須自己創建自己的唯一實例
  3. 單例類必須給所有其他對象提供這一實例
單例模式確保某個類只有一個實例,而且自行進行實例化並向整個系統提供這個實例。在計算機系統中,某些東西被設計成爲單例模式。例如線程池、緩存、日誌對象、對話框、打印機、顯卡的驅動程序對象等。這些應用都或多或少具有資源管理器的功能。每臺計算機都可以有若干打印機,但是只能有一個Printer Spooler,以避免兩個打印作業同時輸出到打印機中。每臺計算機可以有若干通信端口,系統應當幾種管理這些通信端口,以避免通信端口同時被兩個請求同時調用。總之,選擇單例模式就是爲了避免不一致狀態。

線程安全的概念

線程安全就是指的是如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能同時運行這段代碼,如果每次運行結果和單線程運行結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。或者說:一個類或者程序所提供的接口對於線程來說是原子操作或者多個線程之間的切換不會導致該接口的執行結果存在二義性,也就是說我們不用考慮同步的問題。

幾種單例模式的實現與比較(懶漢,餓漢,登記)


1.懶漢式單例,先上代碼(只適用於單線程,不好)

/**
 * 
 * <p>description:懶漢,線程不安全</p>
 * @author AbnerLi
 * @date 2017年10月12日上午10:56:50
 */
public class LazySingleton {
	private static LazySingleton lazySingleton = null;
	/**
	 * 這裏的構造方法設計成爲private 就是防止外部對這種單例進行實例化。(其實也能進行實例化,就是通過java反射機制,可以修改訪問權限)
	 */
	private LazySingleton(){
		
	}
	/**
	 * <p>description:獲取實例的方法</p>
	 * @author AbnerLi
	 * @date 2017年10月12日上午11:33:26
	 * @return
	 */
	public static LazySingleton getInstance(){
		if(lazySingleton == null){
			lazySingleton = new LazySingleton();
		}
		return lazySingleton;
	}
}
上面就是懶漢式單例,這種寫法lazy loading很明顯,但是致命的是在多線程不能正常工作,是線程不安全的,當多線程的時候,兩個線程同時運行到判斷lazySingleton是否爲空的if語句,並且lazySingleton確實沒有被創建好,那麼兩個線程都會創建一個實例。經過改進,將上面獲取實例的方法加上上關鍵字synchornized,可以使得在多線程環境下運行。

在方法上面加上synchornized關鍵字進行同步
	public static synchronized LazySingleton getInstance(){
		if(lazySingleton == null){
			lazySingleton = new LazySingleton();
		}
		return lazySingleton;
	}
但是上面這種加synchronized關鍵字進行同步也不是很好,原因就是,每次通過getInstance方法取得lazySingleton實例的時候當前線程都會試圖去獲取一個同步鎖。Be known to us(衆所周知),加鎖是一個非常耗時間和性能的,所以如果能夠想到更好的辦法,能避免就避免。懶漢式還可以進過改進得到如下稍微可行的方式。

雙重檢查鎖定
	public static LazySingleton getInstance(){
		if(lazySingleton == null){//如果爲空纔去獲取同步鎖
			synchronized (LazySingleton.class) {
				if(lazySingleton == null){
					lazySingleton = new LazySingleton();
				}
			}
		}
		return lazySingleton;
	}
上面這種方式優化了懶漢式單例,只有當lazySingleton爲null的時候採取獲取同步鎖,創建一次實例。當實例被創建,後面就不需要試圖加鎖了。但是上面獲取實例都需要進行加鎖的,有沒有不需要加鎖的呢?當然有

靜態內部類
	/**
	 * 靜態內部類,效率最高,不用同步
	 */
	
	private static class LazyHolder{
		private static final LazySingleton instance = new LazySingleton();
	}
	
	public static final LazySingleton getStaticClassLazySingleton(){
		return LazyHolder.instance;
	}
這種方式比上面所有方式好一些,即實現了線程安全,同時又避免了同步帶來的性能影響。

2.餓漢式單例(建議使用)

先上代碼
/**
 * 
 * <p>description:餓漢式單例類,在類的初始化時就自行實例化了,因爲java的靜態變量,在類的加載的時候實例化</p>
 * @author AbnerLi
 * @date 2017年10月13日上午9:44:35
 */
public class HungrySingleton {
	//私有的構造方法
	private HungrySingleton(){
		
	}
	//自行實例化
	private static final HungrySingleton instance = new HungrySingleton();
	//靜態工廠方法
	public static HungrySingleton getInstance(){
		return instance;
	}
}
餓漢式單例,採用的式靜態屬性,因爲在java中類中的靜態成員會隨着類的加載而加載。因此可以直接調用HungrySingleton

3.登記式單例(建議使用)

/**
 * 
 * <p>description:登記式單例,類似於Spring裏面的方法,將類名註冊,下次從裏面直接獲取</p>
 * @author AbnerLi
 * @date 2017年10月13日上午10:28:53
 */
public class SignInSingleton {
	private static Map<String, SignInSingleton> map = new HashMap<String, SignInSingleton>();
	/**
	 * 靜態塊,將實例放入map中
	 */
	static{
		SignInSingleton instance = new SignInSingleton();
		map.put(instance.getClass().getName(), instance);
	}
	//保護的默認構造方法
	protected SignInSingleton() {
		// TODO Auto-generated constructor stub
	}
	
	//靜態工廠方法,返還此類唯一的實例
	public static SignInSingleton getInstance(String name){
		//獲取完整類名
		if(name == null){
			name = SignInSingleton.class.getName();
			System.out.println("name == null" + "---->name=" + name);
		}
		//map中沒有實例,就創建一個
		if(map.get(name) == null){
			try {
				map.put(name, (SignInSingleton)Class.forName(name).newInstance());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return map.get(name);
	}
	
	//一個示意性的方法
	public String about(){
		return "Hello , I am RegSingleton.";
	}
	
	public static void main(String [] args){
		SignInSingleton signInSingleton = SignInSingleton.getInstance(null);
		System.out.println(signInSingleton.about());
	}
}

登記式單例事實上式維護了一組單例類的實例,將這些實例存放在一個Map(登記薄)中,對於已經登記過的實例,則從Map直接返回,對於沒有等級的,先登記然後再返回。

發佈了40 篇原創文章 · 獲贊 49 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章