为什么我们不要求同步 lazy-init getter(holder 惯用语)?

Why don't we require a lazy-init getter to be synchronized (the holder idiom)?

我正在阅读 J. Bloch 的 effective Java,现在我正在阅读有关惰性初始化的部分。考虑以下 class:

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();
    }
}

J。布洛赫谈到这个成语:

The beauty of this idiom is that the getField method is not synchronized and performs only a field access, so lazy initialization adds practically nothing to the cost of access.

不明白为什么安全通过。如果在字段初始化期间另一个线程尝试并发访问该字段怎么办?该对象不是在线程需要它时创建的。那么,会发生什么?

这种方法基于JVM 的初始化原理。

只有在 Holder class 加载后才会创建 Object 的实例。 Holder class 的加载将由 classloader 在您的应用程序中第一次引用字段 o 期间执行(这里是 getObject 方法)。类加载是非同步的,所以这个模式保证:

  1. Object 实例将按需创建(惰性初始化)。
  2. Object 的创建将是线程安全的。

摘自wiki article:

Since the class initialization phase is guaranteed by the JLS to be serial, i.e., non-concurrent, no further synchronization is required in the static getInstance method during loading and initialization.


根据评论更新:

@St.Antario 找到 formal JLS explanation 此功能:

The procedure for initializing C is then as follows: Synchronize on the initialization lock, LC, for C. This involves waiting until the current thread can acquire LC.