理解Gregory Kick在dagger2 ScopedProvider中的延迟初始化单例

时间:2021-09-05 00:36:33

I am reading Dagger2's source code, and in ScopedProvider there is static field UNINITIALIZED which is to indicate that the instance field is not yet initialized. I've already read and understand the double-check-lazy-initialization idiom, but why is it necessary to define the UNINITIALIZED field instead of just using null? Is it something about the JVM?

我正在阅读Dagger2的源代码,在ScopedProvider中有静态字段UNINITIALIZED,表示实例字段尚未初始化。我已经阅读并理解了双重检查 - 惰性初始化习惯用法,但为什么有必要定义UNINITIALIZED字段而不是仅使用null?它是关于JVM的吗?

/**
 * A {@link Provider} implementation that memoizes the result of a {@link Factory} instance.
 *
 * @author Gregory Kick
 * @since 2.0
 */
public final class ScopedProvider<T> implements Provider<T> {
    private static final Object UNINITIALIZED = new Object();

    private final Factory<T> factory;
    private volatile Object instance = UNINITIALIZED;

    private ScopedProvider(Factory<T> factory) {
        assert factory != null;
        this.factory = factory;
    }

    @SuppressWarnings("unchecked") // cast only happens when result comes from the factory
    @Override
    public T get() {
        // double-check idiom from EJ2: Item 71
        Object result = instance;
        if (result == UNINITIALIZED) {
            synchronized (this) {
                result = instance;
                if (result == UNINITIALIZED) {
                    instance = result = factory.get();
                }
            }
        }
        return (T) result;
    }

    /** Returns a new scoped provider for the given factory. */
    public static <T> Provider<T> create(Factory<T> factory) {
        if (factory == null) {
            throw new NullPointerException();
        }
        return new ScopedProvider<T>(factory);
    }
}

1 个解决方案

#1


2  

It looks like it's a lazy null-safe initialization. Imagine if the factory returned null and that code, instead of UNINITIALIZED, was using null as the sentinel value. Every time get() is called it would enter the synchronized block because it doesn't know the null is has is actually the factory result and not just an uninitialized state.

它看起来像是一个惰性的空安全初始化。想象一下,如果工厂返回null并且该代码(而不是UNINITIALIZED)使用null作为sentinel值。每次调用get()时都会进入synchronized块,因为它不知道null is has实际上是工厂结果,而不仅仅是未初始化状态。

This code allows the factory to return null and have the result be correctly memoized in the volatile field so that subsequent reads don't incur the full synchronized overhead.

此代码允许工厂返回null并在volatile字段中正确地记录结果,以便后续读取不会产生完全同步的开销。

#1


2  

It looks like it's a lazy null-safe initialization. Imagine if the factory returned null and that code, instead of UNINITIALIZED, was using null as the sentinel value. Every time get() is called it would enter the synchronized block because it doesn't know the null is has is actually the factory result and not just an uninitialized state.

它看起来像是一个惰性的空安全初始化。想象一下,如果工厂返回null并且该代码(而不是UNINITIALIZED)使用null作为sentinel值。每次调用get()时都会进入synchronized块,因为它不知道null is has实际上是工厂结果,而不仅仅是未初始化状态。

This code allows the factory to return null and have the result be correctly memoized in the volatile field so that subsequent reads don't incur the full synchronized overhead.

此代码允许工厂返回null并在volatile字段中正确地记录结果,以便后续读取不会产生完全同步的开销。