茴香豆的九種寫法——論java單例寫法

懶漢式

public class LazySingleton {
    private static LazySingleton singleton;
    private LazySingleton() {}
    public synchronized LazySingleton getInstance() {
        if (singleton == null) {
            singleton = new LazySingleton();
        }
        return singleton;
    }

}

餓漢式

public class HungarySingleton {
    private static HungarySingleton singleton = new HungarySingleton();

    private HungarySingleton(){}

    public static HungarySingleton getInstance(){
        return singleton;
    }
}

雙重檢查鎖

public class DoubleCheckSingleton {

    private static volatile DoubleCheckSingleton singleton;

    private DoubleCheckSingleton() {}

    public static DoubleCheckSingleton getSingleton() {
        if (singleton != null) {
            return singleton;
        } else {
            synchronized (DoubleCheckSingleton.class) {
                if (singleton == null) {
                    singleton = new DoubleCheckSingleton();
                }
                return singleton;
            }
        }
    }
}

使用靜態內部類

public class StaticInnerClassSingleton {

    private StaticInnerClassSingleton(){}

    private static class StaticInnerClass{
        private static StaticInnerClassSingleton singleton = new StaticInnerClassSingleton();
    }

    public StaticInnerClassSingleton getInstance(){
        return StaticInnerClass.singleton;
    }
}

使用枚舉

public class EnumSingleton {

    private enum  Singleton{
        INSTANCE;
        EnumSingleton enumSingleton;
        Singleton(){
            synchronized (this){
                enumSingleton = new EnumSingleton();
            }
        }
    }
    public EnumSingleton getInstance(){
        return Singleton.INSTANCE.enumSingleton;
    }

}

各類寫法的比較

  • 懶漢式:提供了延遲初始化,不使用的時候不會初始化,可是效率不高,每次進來都要加鎖,競爭激烈的狀況下效率降低嚴重。
  • 餓漢式:借用了JVM classloader機制,這種方式類加載較慢,但在使用的時候速度會比較快。
  • 枚舉:《effective java》中提的方式,簡潔,而且無償提供了序列化機制,絕對的防止了屢次實例化,即時是面對各類序列化或反序列化攻擊,推薦使用。
  • 靜態內部類方式:使用靜態內部類的方式,能夠借用JVM的classload機制,保證單例,在classloader內部實際上也是經過加鎖的方式實現的。
  • 雙重檢查鎖:這個在《java併發編程的藝術》中有說起,若是DoubleCheckSingleton沒有被volatile修飾,有可能會產生問題,產生問題的根源在於 singleton = new DoubleCheckSingleton(); 這一行。這個能夠理解爲以下三行僞代碼
memory = allocate();//一、分配對象的內存空間
ctorInstance(memory);//二、初始化對象
instance = memory//三、設置instance指向剛剛分配的內存地址

在上面3行僞代碼2和3之間,可能會被重排序。重排序以後的順序可能變爲 1,3,2。在JVM規範中容許在單線程內,不會改變單線程程序執行結果的重排序。這個重排序在沒有改變單線程執行結果的前提下能夠提升程序的執行效率。可是這種寫法在多線程中會出現問題,if (singleton == null) 進行了判斷,可是若是線程T1,限制性了3,這時候instance不等於null,T2進來以後判斷singleton!=null,開始拿到這個對象直接使用,可是這個singleton對象尚未被初始化(ctorInstance)。那就是致使T2使用這個對象的時候出錯。以上就是若是singleton沒有加volatile致使的結果。因爲加了volatile,會禁止指令重排序,就不會出現上面的問題。因此這種寫法是安全的。java

相關文章
相關標籤/搜索