有没有更好的方法来缓存一些非常大的对象,这些对象只能创建一次,因此需要缓存?目前,我有以下内容:
public enum LargeObjectCache {
INSTANCE;
private Map<String, LargeObject> map = new HashMap<...>();
public LargeObject get(String s) {
if (!map.containsKey(s)) {
map.put(s, new LargeObject(s));
}
return map.get(s);
}
}
有几个类可以使用LargeObjects,这就是为什么我决定使用单例来表示缓存,而不是将LargeObjects传递给每个使用它的类。
此外,地图不包含很多键(一个或两个,但键可能在程序的不同运行中有所不同)所以,在这种情况下是否还有另一个更有效的地图?
答案 0 :(得分:4)
您可能需要线程安全性以确保您没有两个相同名称的实例。 它对小地图很重要,但你可以避免一次通话,这会使它更快。
public LargeObject get(String s) {
synchronized(map) {
LargeObject ret = map.get(s);
if (ret == null)
map.put(s, ret = new LargeObject(s));
return ret;
}
}
答案 1 :(得分:2)
正如已经指出的那样,您需要解决线程安全问题。简单地使用Collections.synchronizedMap()并不能使它完全正确,因为代码需要复合操作。同步整个块是一种解决方案。但是,如果它很关键,使用ConcurrentHashMap将导致更多并发和可伸缩的行为。
public enum LargeObjectCache {
INSTANCE;
private final ConcurrentMap<String, LargeObject> map = new ConcurrentHashMap<...>();
public LargeObject get(String s) {
LargeObject value = map.get(s);
if (value == null) {
value = new LargeObject(s);
LargeObject old = map.putIfAbsent(s, value);
if (old != null) {
value = old;
}
}
return value;
}
}
您需要在此表单中使用它才能获得正确且最有效的行为。
如果你必须确保只有一个线程甚至可以实例化给定键的值,那么就必须转向类似Google Collections中的计算地图或者Brian Goetz的书“Java Concurrency in Practice”中的memoizer示例。