Java 多線程下的單例模式的設計

單例模式我們在代碼設計中會經常用到。但是在多線程情況下,如果沒有貼別處理,往往結果並非我們所期望的。一般單例模式的代碼如下:
public class Singleton{
    private static Singleton sInstance = null;
    private Singleton(){
        //init
    }
    public static Singleton getInstance(){
        if ( sInstance == null){
            sInstance = new Singleton();
        }
         return sInstance;
    }
}

這塊代碼在單線程中時沒有問題的,但是假設有兩個線程同時調用getInstance()方法,兩個線程進入if後發現說Instance是null,都會去實例化一個Singleton對象,這樣的話就不是單例了。下面就詳細分析一下幾種解決方案。

方案一:加載時就實例化。

private static Singleton sInstance = new Singleton();
這種方法違背了java的lazy-load原則,即使用時才加載,本例中sInstance的所在類和sInstance的類型一樣,就不存在這個問題了,但是,假若Singleton的構造方法中有比較耗時的造作時,就會大大加大了類加載的時間。

方案二:synchronized關鍵字,代碼修改如下:

public class Singleton{
    private static Singleton sInstance;
    private Singleton(){
        //init
    }

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

這種寫法的確能夠保證同步,但是每次在獲取實例的時候,都需要獲取同步鎖,這就比較耗時,效率不高。於是,就有了方案三和方案四。

方案三:減少獲取同步鎖的次數

    public static getInstance(){
        if (sInstance == null){
            syncInit();
        }
        return sInstance;
    }
    private void synchronized syncInit(){
        if (sInstance == null){
            sInstance = new Singleton();
        }
    }

這種方案和方案二相比,只在單例沒有被實例化的時候纔會去競爭同步鎖,大大的降低了時間的消耗。

方案四:利用JVM在加載class的時候是同步的原理

public class Singleton{
    private static class SingletonContainer{
        static Singleton sInstance = new Singleton();
       }
      private Singleton(){
		//init
      }
      public static getInstance(){
               return SingletonContainer.sInstance;
       }
}



JVM在加載class的時候有一種lazy-load機制,即在需要的時候纔去加載,所以當我們第一次調用getInstance方法的時候,JVM纔會去加載SigletonContainer類,並且JVM有同步機制保證SigletonContaner類只被加載一次,這樣就保證了sIntance只被實例化一次。









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