我们能以类似于静态单例的方式轻松地惰性加载Java成员吗



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

public class Bob {
    private static class SingletonWrapper {
        private static final Bob instance = new Bob();
    }
    public static Bob getInstance() {
       return SingletonWrapper.instance;
    }
}

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

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

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

最新更新