我们能否以类似于静态单例的方式轻松地延迟加载 Java 成员?

Can we get painless lazy loading of a Java member in a similar way to the way we can with static singletons?

在 Java 中对静态单例进行惰性初始化时,您可以这样做:

public class Bob {

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

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

}

因为内部 class 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;
    }

}

有什么方法可以在 Bob 中拥有 Jane 的实例,并且线程安全地只在需要时创建实例,而无需使用双重检查锁定或 AtomicReference。理想情况下,get 方法应保持与这些示例中的方法一样简单,但如果这不可能,那么最简单和最快(最有效)的 get 方法执行将是理想的。

不,没有像初始化 类 那样的实例化类型同步规则。你必须自己添加它们。是否使用双重检查锁定或其他一些机制由您决定。

从Java8开始,我喜欢用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
    }
}

还有 用于在没有线程安全约束的情况下进行延迟初始化。我无法将它们完全适应多线程上下文。

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

可以使用Guava的缓存:

public class Bob {
    private final static Object KEY = new Object();

    private final Cache<Object, Jane> cache = 
        CacheBuilder.newBuilder()
                    .build(new CacheLoader<Object, Jane>() {
                               @Override
                               public Jane load() {
                                   return new Jane();
                               }
                           });

    public Jane getJane() {
        return cache.get(KEY);
    } 

}