java基礎學習筆記——單例模式

有很多場景,都需要保證一個類僅有一個實例,爲此有一種專門的設計模式:單例模式。

單例模式的幾種寫法

1. 餓漢式(沒有延遲加載,線程安全)

public class Singleton {
	private static Singleton instance = new Singleton();
	
	public static Singleton getInstance()
	{
		return instance;
	}
}

2. 靜態內部類(延遲加載,線程安全)

public class Singleton {

	private static class SingletonHolder {
		private static final Singleton INSTACE = new Singleton();
	}

	public static Singleton getInstance() {
		return SingletonHolder.INSTACE;
	}
}

3. 懶漢式(有延遲加載,線程不安全)

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

懶漢安全式(延遲加載,線程安全,效率低)

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

加了關鍵字synchronized之後,能保證多線程安全,但是卻做了很多無用功,實際只有第一次初始化的時候需要同步。
加以改進,便產生了雙重校驗方式。

5.雙重校驗方式(延遲加載,線程安全,效率OK)

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

爲了避免每次都同步的性能損耗,做了兩次null檢查。確保只有第一次調用單例的時候纔會做同步。
但是這在java中可能有問題,因爲同步塊外面的null檢查可能看到已經存在但不完整的實例。
因爲java平臺內存模型允許無序寫入和重排序。在構造函數執行之前,變量instance可能就已經變成非null的了。
Java 5之後可以用volatile關鍵字解決這一問題。

6. 枚舉(目前最好的方式)

public enum Singleton
{
	INSTANCE;
	//other methods
}

枚舉沒有延遲加載的問題,也能避免線程同步問題,還能解決單例類序列化問題,目前被認爲是最好的方式,但是感覺生疏,不易理解。

參考:

爲什麼我牆裂建議大家使用枚舉來實現單例。

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