正確使用雙重檢查鎖(DCL)

不少人熟知單例模式中有一種寫法是使用雙重檢查鎖實現的,可是在網上看到的例子基本上都不正確,有些寫是正確,但沒有很好解析,形成不少人沒有真正理解。其中,典型錯誤寫法是這樣的: java

public class Resource {

	private static Resource resource ;
	
	public static Resource getInstance(){
		
		if(resource == null ){
			synchronized (Resource.class) {
				if(resource  == null ){
					resource  = new Resource() ;
				}
			}
			
		}
		
		return resource ;
	}
	
	private Resource(){}

}

它基本思路是,首先在沒有同步的狀況下檢查resource是否等於null,若是條件不成立,直接返回resource 。不然,就使用同步再檢查resource是否等於null,條件成立才正真初始化resource。這中方式既保證只有一個線程初始化resource,又能作到延時加載。彷佛是「魚和熊掌可兼得「。 編程

上面程序真正的問題是沒有同步的狀況下讀取共享變量resource,併發的狀況下對象的狀態值有多是過時無效的。要解決這個問題也很簡單,把resource聲明爲volatile類型。volatile有什麼做用?引用《java併發編程實戰》的解析: 設計模式

當一個域聲明爲volatile類型後,編譯器與運行時會監視這個變量:它是共享的,並且對它的操做不會與其餘的內存操做一塊兒被重排序。volatile變量不會緩存在寄存器或緩存在對其餘處理器隱藏的地方。因此,讀一個volatile類型的變量時,總會返回由某一線程所寫入的最新值。


讀取volatile變量比讀取非volatile變量的性能幾乎沒有差異,不過須要注意的是volatile只能保證內存可見性,並不能保證原子性。 緩存

《effective java 》是不支持這種寫法的,推薦使用」惰性初始化holder「或」枚舉「技巧,如: 併發


public class Resource {
	
	private static  class ResourceHolder{
		private static Resource resource = new Resource() ;
	}

	private static Resource resource ;
	
	public static Resource getInstance(){
		
		return ResourceHolder.resource ;
	}
	
	private Resource(){}

}





參考資料 性能

《effective java 》第二版 spa

《java併發編程實戰》 線程

《研磨設計模式》 設計

相關文章
相關標籤/搜索