何爲單例,就是在一個應用程序中只能有一個實例,就是保證對象只能被new一次。java
懶漢我以爲這個名字很形象,就是很懶,因此別的對象加載,它就不加載,你調用個人時候我在加載。比喻hibernate中也有懶模式。ok咱們開始吧面試
一天小明去面試,面試官說,你給我寫個單例模式,小明一想這實在太簡單了不暇思索很快寫出來了以下的單例模式數據庫
1 public class Singleton { 2 private static Singleton singleton; 3 public static Singleton getSingleton() 4 { 5 if(singleton==null) 6 { 7 singleton=new Singleton(); 8 } 9 return singleton; 10 } 11 }
而後面試官一看說:你這在高併發的時候有可能會產生多個singleton實例,小明一想怎麼會呢,面試官解釋說,若是有2個線程T1,T2同時執行,當T1執行到第6行的時候,時間片斷到,系統開始讓T2執行,執行到第9行,而後T1又開始執行,由於T1已經作過判斷此時並不知道singleton已經被實例化,因此singleton此時再次被實例化,這樣你係統就有2個singleton對象,那仍是單例嗎,小明恍然大悟,這個我能解決,立刻又寫出下面這個安全
1 public class Singleton { 2 private static Singleton singleton; 3 4 public synchronized static Singleton getSingleton() 5 { 6 if(singleton==null) 7 { 8 singleton=new Singleton(); 9 } 10 return singleton; 11 }
面試官一看,加上了線程同步,這個時候確實能保證線程安全問題,可是又提出了疑問,若是如今singleton已經被實例化了,若是10個線程同時訪問,每次都要等待那麼勢必形成性能極大的消耗,你有沒有別的方案解決問題,小明思考一分鐘又寫下了下面一段代碼併發
1 public class Singleton { 2 private static Singleton singleton; 3 4 public static Singleton getSingleton() 5 { 6 if(singleton==null) 7 { 8 synchronized (Singleton.class) { 9 if(singleton==null) 10 { 11 singleton=new Singleton(); 12 } 13 } 14 } 15 return singleton; 16 }
面試官一看,果然在上面一段代碼的基礎上提高了很多性能,減小了沒必要要的等待,可是仔細一看說你這代碼有點問題,並不能保證線程的安全,小明說怎麼說呢,而後面試官解釋說:若是有T1,T2兩個線程,T1線程運行第六行發現singleton==null,就進入第8行,開始對singleton進行實例化,由於實例化中分爲三步,第一步爲對象開闢內存空間,第二步爲對象初始化,第三步是把這個內存地址賦給singleton,可是由於java的內存模式容許無序寫入,這樣一來會致使第二步和第三步位置調換,那麼這樣一來就壞了,若是先容許第一步和第三步了,可是此時並無對對象進行初始化,偏偏在此時T2進入了第6行,通過判斷singleton不爲null,那麼就會返回一個沒有被初始化的對象。小明聽了以爲對啊,他說我把內存模式改成不容許無序寫入不就好了嗎,因而就把代碼修改成高併發
1 public class Singleton { 2 private volatile static Singleton singleton;//表示有序寫入 3 4 public static Singleton getSingleton() 5 { 6 if(singleton==null) 7 { 8 synchronized (Singleton.class) { 9 if(singleton==null) 10 { 11 singleton=new Singleton(); 12 } 13 } 14 } 15 return singleton; 16 }
面試官又問你知道餓漢模式怎麼寫的嗎,小明一聽:哦餓漢,不就是很着急本身立刻進行實例化,生怕本身沒法實例化嗎,這個簡單立刻寫了一個餓漢性能
1 public class Singleton { 2 private static Singleton singleton=new Singleton(); 3 public static Singleton getSingleton() 4 { 5 return singleton; 6 } 7 }
小明一看上面的模式,本身突發奇想,餓漢模式着急建立對象,在加載時候消耗性能,而懶漢模式又存在線程安全問題(優化後沒有了)能不能結合一下呢,忽然告訴面試官我還有一個比較好的方式來實現,而後他寫了下面代碼單元測試
1 public class Singleton { 2 private static class SingletonManager{ 3 private final static Singleton SINGLETON=new Singleton(); 4 } 5 public final static Singleton getSingleton() 6 { 7 return SingletonManager.SINGLETON; 8 }
面試官一看,不錯不錯,既保證了懶加載,同時也保證了線程安全問題。測試
面試官又問小明,那麼你知道使用場景嗎,小明想了想說,既然在應用程序中只有一個單例,那麼勢必是用於共享資源,比喻數據庫鏈接池,線程池等均可以用單例模式。優化
面試官繼續問:靜態類一樣也是產生一個對象,和單例具備高度類似你知道他們區別嗎,小明回答說
1:面向對象中有三大特性繼承,封裝和多態,可是靜態類是不能夠繼承的,因此從oo角度來講靜態類並不符合面向對象,他們的類是不能夠被覆蓋,因此靈活性要比單例差的多
2:因爲靜態類的特殊他在編譯器已經進行實例化了並不能提供懶加載模式
3:對於項目中若是進行單元測試,因爲方法不能覆蓋一樣爲測試帶來了困難
4:因爲靜態類在編譯器已經都被實例化,因此要比單例性能要快,若是隻須要執行一些靜態方法這個時候能夠採用靜態類
原來只是簡單的瞭解單例,概念比較模糊,看了這篇博客,對單例模式有了更清晰的認識。也但願對大家有所幫助