引言
有时候,我们需要推迟一些高开销的对象初始化操作,并且只有当使用这些对象时才进行初始化。这让我立马反应到DCL的写法,但很快就被打脸,《Java并发编程实战》中表示不太推荐使用DCL。本文记录安全初始化的几个方式。
PS:fucking-java-concurrency代码解读上次学习了并发知识后,给仓库提交了一个final案例,没想到大佬很快就merge了,还给了我contributor,很开心,欢迎学习和Star!
附上仓库链接:https://github.com/WeiXiao-Hyy/blog
线程安全的延迟初始化
public class SafeLazyInitialization {
private static Resource resource;
public synchronized static Resource getInstance() {
if (resource == null) {
resource = new Resource();
}
return resource;
}
}
getInstance的代码路径很短,因此如果getInstance没有被多个线程频繁调用,那么SafeLazyInitialization上不会存在激烈的竞争,从而能提供令人满意的性能。
静态初始化的对象都不需要显式的同步
静态代码块和静态变量初始化
静态代码块和静态变量初始化在类加载后并且被线程使用之前。并且JVM将在初始化期间获得一个锁,这个锁用于确保类的初始化在多线程环境下是安全的。每个线程至少获取一次锁,可能有多个线程同时尝试使用同一个类,而JVM需要确保类的初始化只被执行一次,在初始化完成之前,其他线程需要等待。
因此无论是在被构造期间还是被引用,静态初始化的对象都不需要显式的同步。
提前初始化
通过使用提前初始化,避免了在每次调用SafeLazyInitialization中的getInstance时所产生的同步开销。
public class EagerInitialzation {
private static Resource resource = new Resource();
public static Resource getResource() {
return resource;
}
}
延长初始化占位类模式
首先明确静态内部类的加载过程
静态内部类的加载不需要依附外部类,在使用时才会加载。同时在加载静态内部类的过程中也会加载外部类。
通过以上理论可以形成一种延迟初始化技术,从而在常见的代码路径并不需要同步。
public