HeadFirst設計模式(五) - 單例模式

獨一無二的對象

    單例模式的類圖能夠說是全部設計模式中最簡單的,事實上,它的類圖上只有一個類!可是可不要興奮過頭,儘管從類設計的視角來講它很簡單,可是實際上仍是會遇到至關多的波折。java

    有一些對象其實咱們只須要一個,比方說:線程池、緩存、對話框、註冊表……事實上,這類對象只能有一個實例,若是製造出多個就會致使許多問題產生。程序員

    許多時候,的確經過程序員之間的約定就能夠辦到。但若是有更好的作法,你們應該都樂意接受。單例模式是經得起時間考驗的方法,能夠確保只有一個實例會被建立。單例模式也給了咱們一個全局的訪問點,和全局變量同樣方便,又沒有全局變量的缺點。設計模式

    舉例來講,若是將對象賦值給一個全局變量,那麼你必須在程序一開始就建立好對象,萬一這個對象很是耗費資源,而程序在此次的執行過程當中又一直沒用到它,就造成了浪費。緩存

經典的單例模式

    咱們正在把某個類設計成本身管理的一個單獨實例,同時也避免其餘類再自行產生實例。想要取得單例實例,經過單例類是惟一的途徑。多線程

    咱們也提供對這個實例的全局訪問店,當你須要實例時,想類查詢,它會返回單個實例。性能

處理多線程

    在多線程下,前面的代碼可能不會運行的很好。好比:若線程A在執行if(uniqueInstance==null)後被掛起(已經進入if的語句塊),切換至線程B,那麼這時uniqueInstance對象仍是null。接下來線程B在調用getInstance()方法建立實例後被掛起,線程A被激活,繼續執行uniqueInstance = new Singleton()這段代碼,那麼此時程序中就會有兩個Singleton的實例。spa

只要把getInstance()變成同步(sunchronized)方法,多線程災難幾乎就能夠輕易地解決了:線程

    經過增長sunchronized關鍵字到getInstance()方法中,咱們迫使每一個線程在進入這個方法以前,要先等候到別的線程離開該方法。也就是說,不會有兩個線程能夠同時進入這個方法。設計

    可是同步會下降性能,其實,只有第一次執行此方法時,才真正須要同步。換句話說,一旦設置好uniqueInstance變量,就再也不須要同步這個方法了。以後每次調用這個方法,同步都是一種累贅。code

能改善多線程嗎?

1.若是getInstance()的性能對應用程序不是很關鍵,就什麼都別作。

若是你的應用程序能夠接受getInstance()形成的額外負擔,就忘了這件事吧。同步getInstance()方法既簡單又有效。可是你必須知道,同步一個方法可能形成程序執行效率降低100倍。所以,若是將getInstance()的程序使用在頻繁運行的地方,你可能就得從新考慮了。

2.使用「急切」建立實例,而不用延遲實例化的作法。

    若是應用程序老是建立並使用單例實例,或者在建立運行時方法的附帶不太繁重,你可能想要會想要一下這種實現方式:

public class Singleton {
	// 須要進行單例的對象。
	private static Singleton uniqueInstance = new Singleton();
	
	// 該對象不能使用new進行建立。
	private Singleton() {}
	
	// 對象能夠經過該方法進行建立。
	public static synchronized Singleton getInstance() {
		return uniqueInstance;
	}
}

    利用這個作法,咱們依賴JVM在加載這個類時立刻建立次惟一的單例實例。JVM保證在任何線程訪問uniqueInstance靜態變量以前,必定先建立次實例。

3.用「雙重檢查加鎖」,在getInstance()中減小使用同步。

    利用雙重檢查加鎖(double-checked locking),首先檢查是否實例已經建立了,若是還沒有建立,「才」進行同步。這樣一來,只有第一次會同步,這正是咱們想要的。來看看代碼:

public class Singleton {
	// 須要進行單例的對象。
	private volatile static Singleton uniqueInstance = null;
	
	// 該對象不能使用new進行建立。
	private Singleton() {}
	
	// 對象能夠經過該方法進行建立。
	public static Singleton getInstance() {
		// 若是實例不存在,就進入同步區。
		if(uniqueInstance == null) {
			synchronized (Singleton.class) {
				if(uniqueInstance == null) {
					// 進去區塊後,再檢查一次。若是還是null,才建立實例。
					uniqueInstance = new Singleton();
				}
			}
		}
		return uniqueInstance;
	}
}

    volatile關鍵詞確保,當uniqueInstance變量被初始化成Singleton實例時,多個線程正確地處理uniqueInstance變量。

    若是性能是你關心的重點,那麼這個作法能夠幫助你大大減小getInstance()的時間耗費。

    很不幸地,在1.4及更早版本的java中,許多JVM對於volatile關鍵字的實現會致使雙重鎖檢查加鎖失敗。若是你不能使用Java5,必須使用舊版的Java,就請不要利用此技巧實現單例模式。

相關文章
相關標籤/搜索