Java單例模式

來源:互聯網
上載者:User

標籤:方法   亂序   讀取   空間   let   單例   靜態   gets   推薦   

一、餓漢式

 這種方式基於classloder機制避免了多線程的同步問題

1 public class Singleton {  2     private static Singleton instance = new Singleton();  3     private Singleton (){}  4     public static Singleton getInstance() {  5     return instance;  6     }  7 }

 這種實現方式適合單例佔用記憶體比較小,在初始化時就會被用到的情況。但是如果單例佔用的記憶體比較大,或單例只是在某個特定情境下才會用到,使用餓漢模式就不合適了,這時候就需要用到懶漢模式進行消極式載入。

 

二、懶漢式單例

 

使用靜態內部類

這種方式是Singleton類被裝載了,instance不一定被初始化。因為SingletonHolder類沒有被主動使用,只有顯示通過調用getInstance方法時,才會顯示裝載SingletonHolder類,從而執行個體化instance

並且由於靜態內部類只會被載入一次,所以這種寫法也是安全執行緒的:

1 public class Singleton {  2     private static class SingletonHolder {  3     private static final Singleton INSTANCE = new Singleton();  4     }  5     private Singleton (){}  6     public static final Singleton getInstance() {  7     return SingletonHolder.INSTANCE;  8     }  9 }

 

雙重檢查鎖定

在加鎖前多進行一次null檢查就可以減少絕大多數的加鎖操作,執行效率提高的目的也就達到了。

如果不使用volatile,由於指令重排最佳化的存在,導致初始化Singleton和將對象地址賦給singleton欄位的順序是不確定的。在某個線程建立單例對象時,在構造方法被調用之前,就為該對象分配了記憶體空間並將對象的欄位設定為預設值。此時就可以將分配的記憶體位址賦值給singleton欄位了,然而該對象可能還沒有初始化。若緊接著另外一個線程來調用getSingleton,取到的就是狀態不正確的對象,程式就會出錯。

 1 public class Singleton {   2     private volatile static Singleton singleton;   3     private Singleton (){}   4     public static Singleton getSingleton() {   5     if (singleton == null) {   6         synchronized (Singleton.class) {   7         if (singleton == null) {   8             singleton = new Singleton();   9         }  10         }  11     }  12     return singleton;  13     }  14 }

volatile這個關鍵字就是可見度。可見度指的是在一個線程中對該變數的修改會馬上由工作記憶體(Work Memory)寫回主記憶體(Main Memory),所以會馬上反應在其它線程的讀取操作中。順便一提,工作記憶體和主記憶體可以近似理解為實際電腦中的快取和主存,工作記憶體是線程獨享的,主存是線程共用的。

volatile的第二層語義是禁止指令重排序最佳化。大家知道我們寫的代碼(尤其是多線程代碼),由於編譯器最佳化,在實際執行的時候可能與我們編寫的順序不同。編譯器只保證程式執行結果與原始碼相同,卻不保證實際指令的順序與原始碼相同。這在單線程看起來沒什麼問題,然而一旦引入多線程,這種亂序就可能導致嚴重問題。volatile關鍵字就可以從語義上解決這個問題。

 

使用枚舉

使用枚舉除了安全執行緒和防止反射強行調用構造器之外,還提供了自動序列化機制,防止還原序列化的時候建立新的對象。因此,Effective Java推薦儘可能地使用枚舉來實現單例。

 1 public enum Singleton { 2     INSTANCE; 3     private String name; 4     public String getName(){ 5         return name; 6     } 7     public void setName(String name){ 8         this.name = name; 9     }10 }

 

餓漢式與懶漢式區別

1、餓漢式在類建立的同時就執行個體化一個靜態對象出來,不管之後會不會使用這個單例,都會佔據一定的記憶體,但是相應的,在第一次調用單例時速度也會更快,因為其資源已經初始化完成。
2、懶漢式顧名思義,會消極式載入,在第一次使用該單例的時候才會執行個體化對象出來,第一次調用時要做初始化,如果要做的工作比較多,效能上會有些延遲,之後就和餓漢式一樣了。

如果初始化工作比較多,佔用資源少,調用次數多,建議使用餓漢式,在訪問的時候可以加快調用的速度。

如果某個單例使用的次數少,並且建立單例消耗的資源較多,那麼就需要實現單例的按需建立,這個時候使用懶漢模式就是一個不錯的選擇。

Java單例模式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.