java單例模式的實現

單例模式

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。它有以下三個特點:

  • 單例類只能有一個實例。
  • 單例類必須自己創建自己的唯一實例。
  • 單例類必須給所有其他對象提供這一實例。

創建單例模式

  • 懶漢式(線程不安全)

所謂的懶漢式就是需要使用的時候纔去創建實例。這種非線程安全的創建模式,懶加載很明顯,不能夠滿足多線程條件下使用。

/**
 * 傳統的懶漢式單例
 */
public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 懶漢式(線程安全)

通過synchronized關鍵字可以實現線程安全的懶漢式單例,但是似乎還有更好的方法。

/**
 * 線程安全的懶漢式單例
 */
public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static synchronized Singleton getInstance() {
        if (null == instance) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 餓漢式(線程安全)

餓漢式即在類裝載的時候就進行了實例化。但是很這樣會導致無法懶加載,效率很低,浪費系統資源。

/**
 * 線程安全的餓漢式單例
 */
public class Singleton {
	// 類加載時就初始化
    private static final Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}

餓漢式的創建方式在一些場景中將無法使用:譬如 Singleton 實例的創建是依賴參數或者配置文件的,在 getInstance() 之前必須調用某個方法設置參數給它,那樣這種單例寫法就無法使用了。

  • 雙重校驗鎖

雙重檢驗鎖模式(double checked locking pattern),是一種使用同步塊加鎖的方法。程序員稱其爲雙重檢查鎖,因爲會有兩次檢查 instance == null,一次是在同步塊外,一次是在同步塊內。爲什麼在同步塊內還要再檢驗一次?因爲可能會有多個線程一起進入同步塊外的 if,如果在同步塊內不進行二次檢驗的話就會生成多個實例了。

/**
 * 線程安全的雙重校驗鎖單例
 */
public class Singleton {
    private volatile static Singleton instance;// 1
    private Singleton() {
		something init; 
		// 注意如果不在 1 處加上volatile,(雙重校驗鎖)這裏可能出現空指針異常。
		// 原因是JVM運行時的指令重排序,導致線程2判斷instance不爲空的時候,就返回了上面的init實例變量,但是此時實例變量可能未創建完成。
	}
    public static Singleton getInstance() {
        // 先檢查實例是否存在,如果不存在才進入下面的同步塊  
        if (null == instance) {
	        // 同步塊,線程安全地創建實例  
            synchronized (Singleton.class) {
	            // 再次檢查實例是否存在,如果不存在才真正地創建實例  
                if (null == instance) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 靜態內部類

靜態內部類相比餓漢式創建單例有一處優點,就是創建實例的時候並不是在類裝載的時候。而是主動通過SingletonHolder類,調用getInstance方法的時候,纔會去實例化。這樣就很合理的節省資源。

/**
 * 線程安全的靜態內部類單例
 */
public class Singleton {
    private Singleton() {}
    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }
    public static final Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

這種寫法仍然使用JVM本身機制保證了線程安全問題;由於 SingletonHolder 是私有的,除了 getInstance() 之外沒有辦法訪問它,因此它是懶漢式的;同時讀取實例的時候不會進行同步,沒有性能缺陷;也不依賴 JDK 版本。

  • Holder方式

A類初始化是不會創建A的實例,而是在B類中靜態的聲明瞭對A的實例化,然後再A中,需要實例化A時去調用B。

  • 枚舉方式
發佈了65 篇原創文章 · 獲贊 93 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章