記得剛開始涉足程序的時候, 去筆試 ,發現有一個筆試題常常粗線,寫一個單例模式的基本實現, 當時沒研究設計模式也就不知爲什麼物,java
到今日 , 才發現它已成爲我平常開發最經常使用的一種設計模式。c++
我寫的全部設計模式的代碼都會用java 呈現, 雖然第一個學習的是c++可是 最開始做爲工做的是java,而且有點偏好javac#
單例模式 , 意思就是 整個系統僅只有此類的一個實力, 固然這只是狹義的單例,常常看到變種的單例是容許,建立指定數量的實例的設計模式
單例模式是一種建立型模式。 它是優化的一種策劃, 避免重複建立銷燬一個對象(建立銷燬對象是有開銷的), 有點像對象池的概念。安全
我寫博文 純粹是對本身所瞭解的東西的一個回顧, 不保證權威性。 僅但願 迷途的新手能在個人博文中 有所收穫,我也儘可能 用我最簡單的語言 去描述 我對 不少東西的理解、(這段話會在個人每個博文裏面出現 由於 我不是一個看了書 作筆記的博客, 而是我進行思考的博客 ,固然也但願大牛能夠批評指正)多線程
下面給出一個最簡單的單例的實現函數
1 public class HelloSingleton { 2 3 private static HelloSingleton w_Instance = new HelloSingleton(); 4 5 //定義一個私有的構造函數,確保在該類的外部沒法生成類的實例 6 private HelloSingleton(){ 7 8 } 9 10 public static HelloSingleton getInst(){ 11 return w_Instance; 12 } 13 }
這一種單例模式被成爲餓漢模式, 其實意思就是開始的時候就實例好了 對象, 缺點是沒有作到延遲加載 (延遲加載是一種優化策略,在須要的時候再去加載)這個用起來比較簡便學習
而後下面是 另一種單例模式,懶漢模式優化
public class HelloSingleton { private static HelloSingleton w_Instance; //定義一個私有的構造函數,確保在該類的外部沒法生成類的實例 private HelloSingleton(){ } public static HelloSingleton getInst(){ if(w_Instance == null){ w_Instance = new HelloSingleton(); } return w_Instance; } }
public class HelloSingleton { private static HelloSingleton w_Instance; //定義一個私有的構造函數,確保在該類的外部沒法生成類的實例 private HelloSingleton(){ } public static HelloSingleton getInst(){ if(w_Instance == null){ w_Instance = new HelloSingleton(); } return w_Instance; } }
網上看到,或者大多數人都是如此寫懶漢模式的, 這種寫法在單線程環境下是安全的, 可是在多線程環境下 會形成生成多個實例的問題spa
好比現成A正在訪問 getInst()方法 ,線程A讀到w_Instance 爲null ,建立該類對象並返回,線程B正好在此時也訪問getInst方法,此時現成A構造的實例現成B沒法看見,由於多線程環境中非同步代碼快沒法保證共享變量的可見性。因此該寫法是有問題的, 調整爲以下則不會在多線程環境下出現建立多個實例的問題。
1 public class HelloSingleton { 2 3 private static HelloSingleton w_Instance; 4 5 //定義一個私有的構造函數,確保在該類的外部沒法生成類的實例 6 private HelloSingleton(){ 7 8 } 9 10 public static synchronized HelloSingleton getInst(){ 11 if(w_Instance == null){ 12 w_Instance = new HelloSingleton(); 13 } 14 return w_Instance; 15 } 16 }
1 public class HelloSingleton { 2 3 private static HelloSingleton w_Instance; 4 5 //定義一個私有的構造函數,確保在該類的外部沒法生成類的實例 6 private HelloSingleton(){ 7 8 } 9 10 public static synchronized HelloSingleton getInst(){ 11 if(w_Instance == null){ 12 w_Instance = new HelloSingleton(); 13 } 14 return w_Instance; 15 } 16 }
java用synchronized保證同步代碼快。 同步代碼快 既保證 變量的原子性,也保證變量的可見性。
上面是最簡單的兩種單例的寫法,可是爲了擴展 , 仍是介紹一些 其餘的 常見的單例的寫法。
effective java做者 推崇的 用枚舉的方式實現的 singleton
1 public enum HelloSingleton { 2 3 Instance; 4 5 }
額 你沒有看錯 就是這個樣子的 , 起初 看到這個代碼 , 我也很疑惑 , 這個是單例嗎, 可是確實它也實現了單例 模式。 java的枚舉不一樣於 c++ , c#等語言, java的枚舉是用對象實現的, 因此此枚舉裏 僅有一個對象
它的優勢是這麼介紹的 優勢:不只能避免多線程同步問題,並且還能防止反序列化從新建立新的對象
第四種單例,採用靜態內部類型, 個人同時 不少次 跟我講 他以爲java的內部類真的是很扯淡的設計, 不知道是幹什麼用的, 可是我我的感受java的內部類讓java 語言用起來更靈活, 好比我 不想讓別人使用的類 僅僅想在一個對象裏面使用的類 ,就能夠用private 定義一個 內部類 , 別人無須知道 內部實現。 更好的實現封裝的思想,
代碼示意圖以下
1 public class HelloSingleton { 2 private static class SingletonHolder 3 { 4 private final static HelloSingleton w_Instance = new HelloSingleton(); 5 } 6 7 private HelloSingleton(){ 8 9 } 10 11 public static HelloSingleton getInst(){ 12 return SingletonHolder.w_Instance; 13 } 14 }
此類型看起來比較晦澀 好像繞了不燒 , 這樣寫的好處是:加載時不會初始化靜態變量INSTANCE,由於沒有主動使用,達到延遲加載
還有一個類型就不介紹了 ,由於我的 基本沒用到過, 感受本身最經常使用的就是第二種, 懶漢式 , 單例。
設計模式, 單例, 就是這麼簡單 。
關於單例的變種你能夠這麼想 , 有的時候 你會想讓單例 生成指定數量的 實例, 具體實現就是在 類裏面加個 數量, 而後每次getInst 的時候去判斷 是否達到最大數量, 若是達到就不在生成新的實例 ,若是沒有,就生成新的實例返回, 好了 單例 就這麼簡單, 感受沒什麼好說的 就寫到這裏