我们能否以类似于静态单例方式的方式获得Java成员的无痛延迟加载?

Tim*_*m B 7 java lazy-loading

在Java中对静态单例进行延迟初始化时,可以执行以下操作:

public class Bob {

    private static class SingletonWrapper {
        private static final Bob instance = new Bob();
    }

    public static Bob getInstance() {
       return SingletonWrapper.instance;
    }

}
Run Code Online (Sandbox Code Playgroud)

因为内部类SingletonWrapper仅在第一次访问时加载,Bob()所以在getInstance()调用之前不会创建内部类.

我的问题是,是否有任何类似的技巧可用于在非静态上下文中对成员变量进行惰性实例化.

public class Bob {

    // Clearly this doesn't work as not lazy
    private final InnerWrapper wrapper = new InnerWrapper();

    private class InnerWrapper {
        private final Jane jane = new Jane();
    }

    public Jane getJane() {
       return wrapper.jane;
    }

}
Run Code Online (Sandbox Code Playgroud)

有没有办法我们可以有一个Jane内部的实例Bob和线程安全地只有按需创建实例而不使用双重检查锁定或AtomicReference.理想情况下,get方法应该保持与这些示例中的方法一样简单,但如果不可能,那么方法的最简单,最快速(最有效)的执行get将是理想的.

Sot*_*lis 8

不,没有用于实例化类型的同步规则,就像初始化类一样.你必须自己添加它们.无论您是使用双重检查锁定还是其他一些机制,都取决于您.

从Java 8开始,我喜欢ConcurrentHashMap#computeIfAbsent用来实现懒惰.

class Bob {
    private final ConcurrentHashMap<String, Jane> instance = new ConcurrentHashMap<>(1);

    public Jane getJane() {
        return instance.computeIfAbsent("KEY", k -> new Jane()); // use whatever constant key
    }
}
Run Code Online (Sandbox Code Playgroud)

在没有线程安全约束的情况下,还存在这些用于延迟初始化的解决方案.我无法干净地适应多线程环境.

如评论中所述,双重检查锁定将始终比这些解决方案更快,因为它们不包括隐藏实现的所有漏洞.