Java 为什么不';我们不需要同步一个懒惰的init getter(holder习惯用法)?
我正在阅读J.Bloch的有效Java,现在我在读关于延迟初始化的部分。考虑下面的类:Java 为什么不';我们不需要同步一个懒惰的init getter(holder习惯用法)?,java,multithreading,lazy-initialization,Java,Multithreading,Lazy Initialization,我正在阅读J.Bloch的有效Java,现在我在读关于延迟初始化的部分。考虑下面的类: public class LazyInit{ public static getObject(){ //Not synchronized return Holder.o; } private static Object createObject(){ System.out.println("Creating started"); r
public class LazyInit{
public static getObject(){ //Not synchronized
return Holder.o;
}
private static Object createObject(){
System.out.println("Creating started");
return new Object();
}
private static class Holder{
private static Object o = createObject();
}
}
布洛赫谈到这个成语:
这个成语的妙处在于getField方法不是
已同步并且只执行字段访问,所以很懒
初始化实际上不会增加访问成本
我不明白为什么它能安全通过。如果在字段初始化期间,另一个线程尝试同时访问该字段,该怎么办?在线程需要该对象时,该对象不会被创建。那么,接下来会发生什么呢?这种方法基于JVM的初始化原则 只有在加载
Holder
类之后,才会创建对象的实例。在应用程序中第一次引用字段o
时,类加载器将执行Holder
类的加载(这里是getObject
方法)。类加载是非当前的,因此此模式保证:
对象
实例将按需创建(延迟初始化)
创建对象
将是线程安全的
摘自:
由于JLS保证类初始化阶段是串行的,即非并发的,因此在加载和初始化期间,静态getInstance方法中不需要进一步同步
根据评论更新:
@圣安塔里奥发现了这一特征:
初始化C的过程如下:同步C的初始化锁LC。这包括等待当前线程可以获取LC
不清楚。那么,如果在一个线程初始化类的过程中,另一个线程尝试访问它的字段,会发生什么呢。它会被锁定直到初始化完成吗?@St.Antario,这就是诀窍。对象实例将在Holder类初始化期间创建,并且Holder类是非并发初始化的,这意味着第二个线程将和第一个线程一样等待Holder类初始化。实际上,得到了它。下面是我发现的一些:初始化C的过程如下:同步C的初始化锁LC。这包括等待当前线程可以获取LC。如果您在答案中添加了一些正式的引用,可能会对某些人有用。因为类初始化是非并发的,当初始化很昂贵并且可以并行完成时,这也可能是一种反模式。