線程安全的單例模式

方案一:Double Check Lock
public class ThreadSafeSingleton {
    private static ThreadSafeSingleton sThreadSafeSingleton;

    public static ThreadSafeSingleton getInstance() {
        if (sThreadSafeSingleton == null) {
            createSingleton();
        }
        return sThreadSafeSingleton;
    }

    private synchronized static void createSingleton() {
        if (sThreadSafeSingleton == null) {
            sThreadSafeSingleton = new ThreadSafeSingleton();
        }
    }

方案二:內部類延遲加載實例化
public class ThreadSafeSingleton {
public static ThreadSafeSingleton getInstance() {
return ThreadSafeSingletonHolder.sThreadSafeSingleton;
}

private static class ThreadSafeSingletonHolder {
private static ThreadSafeSingleton sThreadSafeSingleton = new ThreadSafeSingleton();
}
}

方案三:類初始化時建立靜態成員
public class ThreadSafeSingleton {
  private static ThreadSafeSingleton sThreadSafeSingleton = new ThreadSafeSingleton();
public static ThreadSafeSingleton getInstance() {
    return sThreadSafeSingleton;
   }
}

測試代碼: public static void main(String[] args) { for (int i = 0; i<3; ++i) { new Thread(new Runnable() { @Override public void run() { TestUtils.print(String.format("thread=%s, singleton=%s", Thread.currentThread().getName(), getInstance())); } }).start(); } }
兩個的輸出結果是同樣的,根據Java Concurrency in practice的理論,方案一存在unsafe publication風險,
即Thread A在初始化sThreadSafeSingleton時,Thread B在讀取該變量,因爲此處讀取操做無鎖,
若compile reorder後,sThreadSafeSingleton的賦值操做在構造函數以前,可能會致使Thread B讀取了一個不完整的sThreadSafeSingleton對象。
而方案二利用內部類延遲加載(調用getInstance時才初始化內部類,而後實例化對象),所以無需鎖操做,性能和安全性上較方案一優。

輸出結果爲:java

thread=Thread-1, singleton=com.tony.ThreadSafeSingleton@142b7711
thread=Thread-2, singleton=com.tony.ThreadSafeSingleton@142b7711
thread=Thread-0, singleton=com.tony.ThreadSafeSingleton@142b7711安全

 

方案2、三的差異在實例化的時機上,方案三在初始化類時便會實例化(如調用ThreadSafeSingleton靜態成員、函數時觸發),ide

方案二在初始化內部類時纔會觸發實例化(主動式引用方式,即調用getInstance,而引用其餘靜態變量或靜態函數時不會觸發內部類初始化,函數

從而實現lazy initialization + thread safe instance的效果)。性能

相關文章
相關標籤/搜索