正确使用双重检查锁(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并发编程实战》 线程

《研磨设计模式》 设计

相关文章
相关标签/搜索