我要求在并发环境中延迟加载资源。加载资源的代码只能执行一次。
Double-checked locking(使用JRE 5+和volatile关键字)和Initialization on demand holder idiom似乎都很适合这项工作。
通过查看代码,初始化按需持有者习惯看起来更干净,更有效(但是,嘿,我在这里猜测)。不过,我必须小心并记录我的每一个单身人士的模式。至少在我看来,很难理解为什么代码就是这样写的......
我的问题是:哪种方法更好?为什么? 如果你的答案是否定的。您将如何在Java SE环境中解决此要求?
替代
我可以使用CDI而不强制它在整个项目中使用吗?那里有文章吗?
答案 0 :(得分:7)
添加另一个,也许更干净的选项。我建议使用枚举变体:
What is the best approach for using an Enum as a singleton in Java?
答案 1 :(得分:7)
就可读性而言,我会选择初始化按需持有者。我认为,双重检查锁定是一种过时的和丑陋的实现。
从技术上讲,通过选择双重检查锁定,您将始终在字段上产生易失性读取,您可以使用初始化按需持有者惯用法进行正常读取。
答案 2 :(得分:7)
按需初始化持有者仅适用于单身人士,您不能拥有每个实例延迟加载的元素。双重检查锁定会给每个必须看课程的人带来认知负担,因为它很容易以微妙的方式出错。我们曾经遇到过各种麻烦,直到我们将模式封装到our concurrency library
中的实用程序类中我们有以下选择:
Supplier<ExpensiveThing> t1 = new LazyReference<ExpensiveThing>() {
protected ExpensiveThing create() {
… // expensive initialisation
}
};
Supplier<ExpensiveThing> t2 = Lazy.supplier(new Supplier<ExpensiveThing>() {
public ExpensiveThing get() {
… // expensive initialisation
}
});
就使用而言,两者都具有相同的语义。第二种形式使内部供应商使用的任何参考资料在初始化后可用于GC。第二种形式也支持TTL / TTI策略的超时。
答案 3 :(得分:5)
初始化按需持有者始终是实施单例模式的最佳实践。它很好地利用了JVM的以下功能。
此外,您不必使用synchronize关键字,它会使您的程序慢100倍。
答案 4 :(得分:3)
我怀疑按需定时器的初始化速度略快于双重检查锁定(使用易失性)。原因是前者在创建实例后没有同步开销,但后者涉及读取一个volatile(我认为)需要完整的内存读取。
如果性能不是一个重要问题,那么同步getInstance()
方法是最简单的。