天天看點

我是架構師--設計模式-單例模式

來次面試吧?準備好沒,GO!

   問:自我介紹下吧。(開個玩笑。。。往下)請問你用過單例模式嗎?什麼是單例?  

答:用過啊,單例模式就是隻建立一個執行個體。  

   問:噢?那是單線程,還是多線程下都是呢?  

答:這個類在JVM裡就一個執行個體(這樣回答也許會更好)  

   (注:通常面試官,到這裡,就會讓你寫一個單例,當然我們不,你看這篇文章,就是不讓你再網上找其他重複的資料了。)  

   問:說說你用的場景吧  

答:........  

   問:說說單例的幾種類型?或者說何時對執行個體的初始化?  

   問:畫過單例的類圖嗎?會畫嗎?  

答:(需要嗎?)  

   問:寫個單例的實作吧?  

 類名:SingleTon

 執行個體:uniqueInstance 簡稱ust吧。(這裡我插入一句程式設計規範:起名不要吝惜把執行個體代表的意思表達清楚,名字可稍微長點,這裡就是想偷懶) 

實作1:

public class Singleton{  

  private final static Singleton ust = new Singleton();  

  private Singleton() {}  

  public static Singleton getInstance(){  

     return ust;  

  }  

}  

注:這裡順便說明一下文法。通常final與static同時出現,習慣讓final在前

分析: 

1. 這裡需要加final嗎? 是的,因為java反射,可以改變private描述的變量

2. 有書裡把這種方式稱為餓漢單例模式(另一個種叫懶漢單例),并且已經為人所接受

3. 在多線程方面表現出了他優勢,不需要擔心方法重複裡延時建立帶來的原子性(這樣說難了解,其實就是出現兩個或多個執行個體)

4. 這個,不符合我們習慣的 用到時再執行個體化的原則(不過這沒關系。。。)

 看,其實你發現這還是個不錯的單例,那麼其實有個最好的實作,最好的實作:

單元素的枚舉類型已經成為Singleton的最佳實踐

即使面對複雜的序列化或者反射攻擊,絕對防止多次執行個體化,還有他的簡潔和優雅。

 ----好吧,面試結束了。 其實你對單例的了解還是不錯的,而且你已經得到了最好的答案,有興趣徹底玩轉單例嗎,繼續聽我唠叨。

    我們看看懶漢單例模式:

實作2:

    // private final static Singleton ust = null;

// 不該加final,這裡明顯有偷懶嫌疑,複制上面的例子,又測試不夠,以後盡量避免類似問題。

// 8月15日修正

private static Singleton ust = null;

     private Singleton(){}  

     public staic Singleton getInstance(){  

//建議null==ust的方式,能幫助更快的發現錯誤。

//部分老程式員的習慣,其實許多IDE會發現些低級錯誤。  

        if(ust==null){//A  

             ust = new Singleton();//B  

         }  

       return ust;  

     }  

}   

分析:

1. 懶漢模式,做到了需要時建立執行個體

2. 他遇到了尴尬的問題,因為當兩個線程分開運作到A,然後進入了if塊,可能就建立了2個執行個體,草稿的是,你已經初始了一些資料。 

改進一下:

實作3: 

private volatile static Singleton ust;  

private Singleton(){}  

public staic Singleton getInstance(){  

synchronized(Singleton.class){  

   if(ust==null){  

    ust= new Singleton();  

   }  

 }  

return ust;  

 分析:

  1. 如果你不了解synchronized 的位置,就不用單例模式這麼多寫法,不如學習基礎

  2. volatile 確定ust被執行個體化後,多個線程正确處理。他失去了JVM必要的代碼優化,如果不是多線程,就不要用

   3. 這個叫做 “雙檢查加鎖”,單例最後一種方式

綜合讨論會: 

小明: 單例目前一共談到懶漢和餓漢兩種,還有雙檢查加鎖,最好的應該是單元素的枚舉類型  

小剛: 是的,回答了開頭說的幾種單例,那麼哪些場景應該用單例呢?  

小明: 我知道,有線程池,緩存,處理偏好設定,系統資料庫,日志對象等等  

小剛: 對,我對java比較了解,我知道Runtime.getRuntime()。   

C(為嗎我叫C):我知道有java.lang.reflect.Proxy類   

小明:有什麼共同點呢,為什麼用?是遵循對象盡量少建立原則? 

C:這是什麼意思?   

小剛:這很簡單,不過這說法有點問題。因為對象占記憶體,有要造成垃圾回收,GC的時候JVM可是隻幹這個麻煩事  

小明:是啊! 

小剛:我想我知道,某些對象最好隻有一個執行個體,多了會有問題産生。   

C:什麼問題?   

小剛:比如緩存,你從哪個執行個體裡拿緩存呢?   

小明:是的啊。。。  

結束讨論會,總結一下吧。不,等等,還要補充兩句: 

1. 單例模式定義:確定一個隻有一個執行個體,并提供一個全局通路點

2. 如果getInstance()方法對應用程式不會額外負擔,或者說影響不大,那寫成怎樣,其實沒太大所謂。但是如果頻繁運作,就要仔細考慮,因為一個同步,可能使得執行效率下降100倍

繼續總結,還差個UML圖呢,不妨在上個枚舉的例子吧,枚舉構造器預設私有嗎? 

枚舉就算了,是不是預設構造器,自己研究下吧。。。呵呵

這回真總結了: 

1. 單例,有懶漢,餓漢,雙檢查加鎖3種常見用法 

2. 單例模式,是因為如果多了,會造成資料遺漏等麻煩 

3. 最好的單例,單元素的枚舉類型

 呵呵,其實,就這些,本來想先寫工廠的,因為去面試,遇到某些對單例了解比較淺,解釋起來費勁,于是先以單例開篇,請關注下篇工廠模式。

本文轉自 wws5201985 51CTO部落格,原文連結:http://blog.51cto.com/yjplxq/931861,如需轉載請自行聯系原作者