設計模式拙見--單例模式

一、前言

單例模式已經是一個非常常見的設計模式,在我們日常開發中也是非常容易遇到,但是你是不是真的掌握了呢?我們一起來探討一下。

二、單例模式實現方式

作爲一名程序員,我們經常在面試的時候被面試官問到,你會寫幾種單例模式?請你講下餓漢模式和飽漢模式的區別?飢漢模式和懶漢模式的區別?雙重校驗鎖的兩個判空作用分別是什麼?哪些實現是線程安全?諸如此類的問題,如果你對單例模式掌握得不夠深,你一定會手忙腳亂,但是如果你掌握了,那麼這一切對你來說都是小菜一碟。

1.懶漢(飽漢)模式和餓漢(飢漢)模式

區別:

  • 懶漢(飽漢)模式:實例在開始時爲空,等到第一次加載時纔會實例化。
  • 餓漢(飢漢)模式:在類加載前就已經實例化。

懶漢(飽漢)模式

/**
 * 懶漢(飽漢)模式
 * 懶加載:是
 * 線程安全:是
 * 描述:既實現了懶加載,也是線程安全,但是效率低,99%的情況下都不需要同步
 * 優點:第一次調用才初始化,避免了內存浪費
 * 缺點:必須加鎖synchronized才能保證同步,但加鎖會影響效率
 */
class LazyDogSingleInstance {
	private static LazyDogSingleInstance sInstance; 
	
	public static synchronized LazyDogSingleInstance getInstance() {
		
		if (sInstance == null) {
			sInstance = new LazyDogSingleInstance();
		}
		return sInstance;
	}
}

餓漢(飢漢)模式

/**
 * 餓漢(飢漢)模式
 * 懶加載:否
 * 線程安全:是
 * 描述:比較常用,但是容易產生垃圾對象
 * 優點:沒有加鎖,執行效率會提高
 * 缺點:類加載時就初始化,浪費內存
 *
 */
class HungryDogSingleInstance {
	private static HungryDogSingleInstance sInstance = new HungryDogSingleInstance();
	
	private HungryDogSingleInstance() {}
	private static HungryDogSingleInstance getInstance() {
		return sInstance;
	}
}

2.實現雙重校驗鎖模式

雙重判空的意義分別是什麼:第一個判空是判斷當前對象是否爲空,判斷是否需要實例化,第二個判空是因爲加了鎖,有可能在等待鎖的時候這個對象已經被實例化,所以獲取鎖的時候需要重新判斷一次這個對象是否爲空。


/**
 * 雙重校驗鎖模式
 * 懶加載:是
 * 線程安全:是
 * 描述:使用了雙重校驗鎖,安全且在多線程下能保持高性能
 *
 */
class DoubleCheckSingleInstance {
	private static DoubleCheckSingleInstance sInstance;
	private DoubleCheckSingleInstance() {
	}
	private static DoubleCheckSingleInstance getInstance() {
		if (sInstance == null) {
			synchronized (DoubleCheckSingleInstance.class) {
				if (sInstance == null) {
					sInstance = new DoubleCheckSingleInstance();
				}
			}
		}
		return sInstance;
	}
}

3.實現靜態內部類

/**
 * 靜態內部類模式
 * 懶加載:是
 * 線程安全:是
 * 描述:利用了類加載機制保證初始化INSTANCE時只有一個線程,在初始化StaticInnerSingleInstance沒有實例化INSTANCE,而是在調用getInstance時才實例化
 *
 */
class StaticInnerSingleInstance {
	private static class Holder {
		private static final  StaticInnerSingleInstance INSTANCE = new StaticInnerSingleInstance();
	}
	private StaticInnerSingleInstance() {}
	
	public static final StaticInnerSingleInstance getInstance() {
		return Holder.INSTANCE;
	}
}

三、單例模式優缺點

  • 全局只有一個對象,對單例類的所有實例化得到都是同一個對象,這樣就可以防止其它對象對自己的實例化,確保所有的對象都訪問一個實例。
  • 單例模式具有一定的伸縮性,類自己來控制實例化進程,類就在改變實例化進程上有相應的伸縮性。
  • 提供了對唯一實例的受控訪問。
  • 由於在系統內存中只存在一個對象,因此可以節約系統資源,當需要頻繁創建和銷燬對象時,單例模式無疑可以提高系統的性能。
  • 允許可變數目的實例。
  • 避免對共享資源的多重佔用。

缺點:

  • 不適用於變化的對象,如果同一類型的對象總是要在不同的用例場景發生變化,單例就會引起數據的錯誤,不能保存彼此的狀態。
  • 由於單例模式中沒有抽象層,因此單例類的擴展有很大的困難。
  • 單例類的職責過重,在一定程度上違背了“單一職責原則”。
  • 濫用單例將帶來一些負面問題,如爲了節省資源將數據庫連接池對象設計爲的單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;如果實例化的對象長時間不被利用,系統會認爲是垃圾而被回收,這將導致對象狀態的丟失。

四、Android中單例模式應用場景

1.AccessibilityManager

public final class AccessibilityManager {
    private static AccessibilityManager sInstance = new AccessibilityManager(null, null, 0);
    public static AccessibilityManager getInstance(Context context) {
        return sInstance;
    }
}

2.LayoutInflater

public class ContextThemeWrapper extends ContextWrapper {
    private LayoutInflater mInflater;

    @Override
    public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章