Java設計模式——單例模式(5種),及其雙重檢查鎖定問題

 餓漢模式

1.將構造方法私有化

2.私有靜態實例化

3.傳出構造完的私有實例

class Singletone {//餓漢模式
	private static Singletone s = new Singletone();//必須在這裏實例化,靜態,私有

	private Singletone() {//必須把構造函數申明爲私有
	}

	public static Singletone getSingleton() {//s爲static,函數也許static
		return s;
	}
}

非線程安全的飽漢模式

先判斷s是否已經初始化

class Singleton{
	private static Singleton s1=null;
	private Singleton(){
		
	}
	public static Singleton getsin(){
		if(s1==null){
			s1=new Singleton();
		}
		return s1;
	}
}

線程安全的飽漢模式

class Singleton{
	private static Singleton s1=null;
	private Singleton(){
		
	}
	public synchronized static Singleton getsin(){//同步方法的
		if(s1==null){
			s1=new Singleton();
		}
		return s1;
	}
}

 減少鎖爭奪的懶漢模式(但有雙重檢查鎖定問題)

class Singleton {
	private static Singleton s1 = null;

	private Singleton() {

	}

	public static Singleton getsin() {
		if (s1 == null) {
			synchronized ("lock") {//上一個方法必須進入同步塊,而本方法避免了不必要的鎖獲取(再判斷不爲空時)
				if (s1 == null) {
					s1 = new Singleton();//問題根源 Q1
				}
			}
		}
		return s1;
	}

}

雖然該方式儘可能避免了鎖爭奪的問題,但是依然會有線程安全問題

Q1行代碼其實可以分解成三步

1.分配s1的內存空間

2.用分配的內存初始化對象

3.將內存對象賦值給s1

其中2.3可能發生重排序,當把內存已經賦值給s1時(也就是s1不爲null時),初始化對象還未完成。因此是不安全的。

解決方案有兩種

1.將s1聲明未volatile(避免重排序)

2.使用類初始化(可重排序,但對外界不可見)

class Singletonclass{
	private Singletonclass(){//單例一定要私有化構造函數!
		
	}
	private static class single{
		public static Singletonclass s1=new Singletonclass();
	}
	public static Singletonclass getsin(){
		return single.s1;
	}
}

具體見併發編程的藝術第三章-三個同步原語-雙重檢查鎖定錯誤分析+兩種安全延遲初始化方案

 

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