單例模式的類圖能夠說是全部設計模式中最簡單的,事實上,它的類圖上只有一個類!可是可不要興奮過頭,儘管從類設計的視角來講它很簡單,可是實際上仍是會遇到至關多的波折。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,就請不要利用此技巧實現單例模式。