不少人熟知单例模式中有一种写法是使用双重检查锁实现的,可是在网上看到的例子基本上都不正确,有些写是正确,但没有很好解析,形成不少人没有真正理解。其中,典型错误写法是这样的: 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并发编程实战》 线程
《研磨设计模式》 设计