有时当我需要懒惰的初始化字段时,我使用以下设计模式。
class DictionaryHolder {
private volatile Dictionary dict; // some heavy object
public Dictionary getDictionary() {
Dictionary d = this.dict;
if (d == null) {
d = loadDictionary(); // costy operation
this.dict = d;
}
return d;
}
}
看起来像Double Checking idion,但不完全是。没有同步,可以多次调用loadDictionary
方法。
当并发性很低时,我使用这种模式。在使用这种模式时,我还要记住以下假设:
loadDictionary
方法始终返回相同的数据。loadDictionary
方法是线程安全的。我的问题:
getDictionary()
是否可以返回无效数据?dict
字段非易失性以提高效率?答案 0 :(得分:3)
最简单的解决方案是依赖于在需要之前不加载类的事实。即无论如何它都是懒惰的。这样你就可以避免自己做这些检查。
public enum Dictionary {
INSTANCE;
private Dictionary() {
// load dictionary
}
}
不应该让它变得更复杂,当然你也不会让它变得更有效率。
编辑:如果Dictionary需要扩展List或Map,你可以这样做。
public enum Dictionary implements List<String> { }
或者更好的方法是使用字段。
public enum Dictionary {
INSTANCE;
public final List<String> list = new ArrayList<String>();
}
或使用静态初始化块
public class Dictionary extends ArrayList<String> {
public static final Dictionary INSTANCE = new Dictionary();
private Dictionary() { }
}
答案 1 :(得分:3)
我个人觉得Initialization on demand holder idiom非常适合这种情况。来自维基:
public class Something {
private Something() {}
private static class LazyHolder {
private static final Something INSTANCE = new Something();
}
public static final Something getInstance() {
return LazyHolder.INSTANCE;
}
}
虽然这可能看起来像纯粹用于单例控制的模式,但你可以用它做更多很酷的事情。对于例如holder类可以调用一个方法,该方法反过来填充某种数据。
此外,在您的情况下,如果多个线程在loadDictionary调用(已同步)上排队,您可能最终会多次加载相同的事件。
答案 2 :(得分:2)
1.这种模式是否正确?换句话说,getDictionary()是否可以返回无效数据?
是的,如果可以同时由多个线程调用loadDictionary()
,那么对getDictionary()
的不同调用可以返回不同的对象。否则,您需要一个具有同步功能的解决方案。
2.是否可以使dict字段非易失性以提高效率?
不,它可能导致内存可见性问题。
3.有没有更好的解决方案?
只要你想要一个没有同步的解决方案(显式或隐式) - 没有(据我所知)。否则,有许多习语,例如使用enum
或内部持有者类(但它们使用隐式同步)。
答案 3 :(得分:2)
快速刺破这个但是......
class DictionaryHolder {
private volatile Dictionary dict; // some heavy object
public Dictionary getDictionary() {
Dictionary d = this.dict;
if (d == null) {
synchronized (this) {
d = this.dict;
if (d == null) { // gated test for null
this.dict = d = loadDictionary(); // costy operation
}
}
return d;
}
}
答案 4 :(得分:2)
您的代码是正确的。为避免加载多次,synchronized{}
会很好。
如果volatile
是不可变的,您可以删除Dictionary
。
private Dictionary dict; // not volatile; assume Dictionary immutable
public Dictionary getDict()
if(dict==null)
dict = load()
return dict;
如果我们添加双重检查锁定,那就完美了
public Dictionary getDict()
if(dict==null)
synchronized(this)
if(dict==null)
dict = load()
return dict;
双重检查锁定对于不可变对象非常有用,无需使用volatile。
不幸的是,上述2 getDict()
方法在理论上并不是防弹的。弱java内存模型将允许一些怪异的行为 - 理论上。要使本书100%正确,我们必须添加一个局部变量,这会使我们的代码混乱:
public Dictionary getDict()
Dictionary local = dict;
if(local==null)
synchronized(this)
local = dict;
if(local==null)
local = dict = load()
return local;
答案 5 :(得分:1)
是否可以使dict字段非易失性以提高效率?
没有。这会损害可见性,即当一个线程初始化dict
时,其他线程可能无法及时(或根本)看到更新的引用。这反过来会导致多次重度初始化,因此很多无用的工作,更不用说返回对多个不同对象的引用。
无论如何,在处理并发时,微观优化效率将是我最后的想法。
答案 6 :(得分:0)
按需初始化持有人类习惯用法
此方法仅依赖于JVM 将班级成员初始化 首先提到这个班级。在这 case,我们有一个内部类 仅在内部引用 getDictionary()方法。这意味着 DictionaryHolder将被初始化 在第一次调用getDictionary()时。
public class DictionaryHolder {
private DictionaryHolder ()
{
}
public static Dictionary getDictionary()
{
return DictionaryLazyHolder.instance;
}
private static class DictionaryLazyHolder
{
static final DictionaryHolder instance = new DictionaryHolder();
}
}