我看的設計模式書是《Head First設計模式》,我決定不按照書上的章節順序作筆記,按照我認爲的容易理解程度從易到難來寫。設計模式
單例模式(Singleton Pattern):確保一個類只有一個實例,並提供一個全局訪問點。緩存
有一些對象其實咱們只須要一個,例如:線程池(threadpool)、緩存(cache)、對話框、處理器偏好設置和註冊表(registry)的對象、日誌對象等。多線程
咱們能夠用Java的靜態變量來坐到這一點,但事靜態全局變量在程序一開始就被建立好對象,若是這個對象很是耗費資源,而程序在此次執行過程當中又一次沒用到它,就造成浪費。性能
經典的單例模式代碼:spa
1 public class Singleton { 2 private static Singleton uniqueInstance; 3 private Singleton(){}//構造器聲明爲私有,只有類內才能夠調用構造器 4 public static Singleton getInstance(){ 5 if(uniqueInstance==null){ 6 uniqueInstance=new Singleton(); 7 } 8 return uniqueInstance; 9 } 10 }
別的類用要用這個類的對象的話,就經過Singleton.getInstance()來獲取,if判斷並且確保了這個類只有一個實例化的對象。這樣彷佛都一切正常了。不過實際項目中,確定會有多線程的場景,那樣就可能產出兩個實例。例如這裏有兩個線程,線程1運行到上述代碼第5行時候,new 一個對象,假如此刻線程2也進入5行,發現此時uniqueInstance爲null,它也new 一個對象,那麼就產生了兩個實例化對象了。因此,要在第4行上面加上synchronized,這樣就保證了不會存在兩個線程同時進入到getInstance方法。線程
在2.1中咱們說過在getInstance方法加入synchronized關鍵字來解決多線程會實例化多個對象的問題,它也存在一些問題:設計
1)同步會下降性能;日誌
2)更嚴重的是:上述代碼只要第一次執行getInstance方法時,才真正須要同步。換句話說,一旦設置好uniqueInstance變量,就再也不須要同步這個方法了。以後每次調用這個方法,同步都是一種累贅。code
下面給出解決三種解決方案:對象
沒錯,就是這麼直接!固然,若是getInstance()在程序中頻繁的運行,那就得從新設計了。
若是程序老是會建立並使用到這個單例類,或者建立這個單例類的實例不繁重,能夠急切的建立此單例:
public class Singleton { private static Singleton uniqueInstance=new Singleton();//在靜態初始化中建立單例 private Singleton(){} public static Singleton getInstance(){ return uniqueInstance; } }
2.2.3 用「雙重檢查加鎖」,在getInstance()中減小使用同步
首先檢查是否實例已經建立了,若是沒有,「才」進行同步。這樣一來,只有第一次建立會用到同步,這正是咱們想要的。
1 public class Singleton { 2 private volatile static Singleton uniqueInstance; 3 private Singleton() {} 4 public static Singleton getInstance() { 5 if (uniqueInstance == null) { 6 synchronized (Singleton.class){ 7 if(uniqueInstance == null){//進入區域後,再檢查一次,若是還是null,才建立實例 8 uniqueInstance = new Singleton(); 9 } 10 } 11 } 12 return uniqueInstance; 13 } 14 }
第7行還須要加入一次判斷,有可能別的線程在此線程拿5-6行的期間,已經實例化了。(ps:個人我的理解)關於volatile關鍵字,不清楚的同窗能夠去查,之後等我看完JVM,會專門寫一篇volatile關鍵字的文章。
一、據說兩個類加載器可能會各自建立本身的單例對象?
答:是的。每一個類加載器都定義了一個命名空間,若是有兩個以上的類加載器,不一樣的類加載器可能會同時加載同一個類,從整個程序來看,同一個類會被加載屢次。若是這樣的事情發生在單件上,就會產生多個單例並存的現象。因此,若是程序有個多個類加載器又同時使用了單例模式,能夠這樣解決:自行指定類加載器,並指定同一個類加載器。
二、全局變量比單例模式差在哪裏?
答:1)不可延遲實例化;2)並不能確保只有一個實例,並且也變相鼓勵開發人員,用許多全局變量指向許多小對象來形成命名空間的污染。