在Java并发编程实践(JAVA concurrency in practice)中的第十六章(Java存储模型)中.
有讲到对象安全发布.
其中就是以单例模式来说明这个安全初始化技巧的.这是学习分析总结如下:
引用
不正确发布带来的风险的真正原因是在"发布共享对象"与从"另一个线程访问它"之间,缺少happens-before排序.
不安全的发布.
发布(Publishing):
引用
发布(Publishing)一个对象的意思是使它能够被当前范围之处的代码所使用.比如将一个引用
存储到其他代码可以访问的地方.在一个非千私有的方法中返回这个引用,也可以把它传递到其他类的方法中.在很多情况下,我们需要确保对象及它们的内部状态不被暴露(publish).
代码清单1:不安全的惰性初始化:
public class UnsafeLazyInitialization{
private static Resource resource;
public static Resource getInstance(){
if(resource == null){
resource = new Resource();
}
return resource;
}
}
引用
除了不可变对象以外,使用被另一个线程初始化的对象,是不安全的,除非对象的发布是happens-before于对象的消费线程使用它.
安全初始化技巧:
代码清单2 线程安全的惰性初始化
public class SafeLazyInitialization{
private static Resource resource;
public synchronized static Resource getInstance(){
if(Resource == null){
resource = new Resource();
}
}
}
代码清单3 主动初始化
public class EagerInitialization{
private static Resource resource = new Resource();
public static Resource getResource() {
return resource;
}
}
像上面那样,使用主动的初始化,避免了每次调用SafeLazyInitialization的getInstance()的同步开销.这项技术可以和JVM的惰性类加载相结合,
创建一种惰性初始化技术,使得在通常的代码路径中都不需要同步.清单4的惰性初始化holder类技巧.使用一个专门用来初始化Resource的类.JVM将ResourceHolder的初始化被 延迟到真正使用它的时刻.因为Resource是在静态初始进行初始化的,所以不再需要额外的同步.,线程第一次调用getResource,引起ResourceHolder的加载和初始化,这个时候,正是静态初始阶段Resource完成初始化发生的时间.
清单4惰性初始化Holder类技巧
public class ResourceFactory{
private static ResourceHolder {
public static Resource resource = new Resource();
}
public static Resource getResource(){
return ResourceHolder.resource;
}
}