Java多線程環境下單例模式實現

單例模式是一種常用的設計模式,單例對象通常作爲程序中的存放配置信息的載體,以保證其他對象讀到一致的信息。

在某個服務器程序中,服務器的配置信息可能存放在數據庫或文件中,其他對象要取得這些信息只需要訪問這個單例就可以。

這個機制在單線程環境下實現簡單,在多線程環境下需要考慮同步問題。


首先,通常使用惰性加載的機制,在單例對象使用的時候纔去創建。


public class Singleton{
    private static Singleton instance=null;
    Private Singleton(){
        ......
    }
    public static Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
            return instance;
        }
}


這樣當第一次調用時單例才被創建。


double-checked locking

對於getInstance()方法,通過對其添加synchronized塊來處理同步問題。

public static Singleton getInstance(){
    if(instance==null){
        synchronized(instance){
            if(instance==null)
                instance=new Singleton();
        }
    return instance;
}

沒有將synchronized加到方法名前,所以同步代碼段只會在最開始執行。


從JVM的角度講,上述代碼仍然可能發生錯誤。在Java指令中創建對象和賦值操作時分開進行的。(指令重排序問題)

假設有線程A和B調用getInstance(),線程A在執行到創建對象步驟後離開,當B進入時instance已經不是null了,但卻還沒有初始化。


用volatile修飾instance的話就可以確保instance = new Singleton();對應的指令不會重排序。


還有一種使用內部類的方式:

  1. public class Singleton{        
  2.     private Singleton(){        
  3.         …        
  4.     }        
  5.     private static class SingletonContainer{        
  6.         private static Singleton instance = new Singleton();        
  7.     }        
  8.     public static Singleton getInstance(){        
  9.         return SingletonContainer.instance;        
  10.     }        
  11. }      
JVM內部的機制能夠保證當一個類被加載的時候,這個類的加載過程是線程互斥的。當第一次調用getInstance的時候,JVM能夠幫我們保證instance只被創建一次,並且會保證把賦值給instance的內存初始化完畢,這樣就排除了只賦值但沒初始化的問題;這個方法也只會在第一次調用的時候使用互斥機制;最後instance是在第一次加載SingletonContainer類時被創建的,而SingletonContainer類則在調用getInstance方法的時候纔會被加載,因此也實現了惰性加載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章