使用基于枚举的单例缓存大型对象(Java)
有没有更好的方法来缓存一些非常大的对象,这些对象只能创建一次,因此需要缓存?目前,我有以下几点:使用基于枚举的单例缓存大型对象(Java),java,design-patterns,oop,hashmap,Java,Design Patterns,Oop,Hashmap,有没有更好的方法来缓存一些非常大的对象,这些对象只能创建一次,因此需要缓存?目前,我有以下几点: public enum LargeObjectCache { INSTANCE; private Map<String, LargeObject> map = new HashMap<...>(); public LargeObject get(String s) { if (!map.containsKey(s)) {
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);
}
}
public enum LargeObjectCache{
实例;
私有映射映射=新的HashMap();
公共大对象获取(字符串s){
如果(!map.containsKey){
地图放置,新的大型对象;
}
返回地图。获取(s);
}
}
有几个类可以使用LargeObject,这就是为什么我决定对缓存使用单例,而不是将LargeObject传递给每个使用它的类
此外,映射不包含许多键(一个或两个,但键在程序的不同运行中可能会有所不同),因此,在这种情况下,是否有另一个更有效的映射可供使用?您可能需要线程安全性来确保没有两个同名实例。 这对小地图来说确实很重要,但您可以避免一次调用,这会使它更快
public LargeObject get(String s) {
synchronized(map) {
LargeObject ret = map.get(s);
if (ret == null)
map.put(s, ret = new LargeObject(s));
return ret;
}
}
您可能需要线程安全性来确保没有两个同名实例。 这对小地图来说确实很重要,但您可以避免一次调用,这会使它更快
public LargeObject get(String s) {
synchronized(map) {
LargeObject ret = map.get(s);
if (ret == null)
map.put(s, ret = new LargeObject(s));
return ret;
}
}
正如已经指出的,您需要解决线程安全问题。简单地使用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;
}
}
public enum LargeObjectCache{
实例;
私有最终ConcurrentMap=新ConcurrentHashMap();
公共大对象获取(字符串s){
大对象值=map.get(s);
如果(值==null){
值=新的较大对象;
LargeObject old=映射。putIfAbsent(s,值);
如果(旧!=null){
价值=旧;
}
}
返回值;
}
}
你需要在这个表单中准确地使用它,以获得正确和最有效的行为
如果您必须确保只有一个线程能够实例化给定键的值,那么就有必要求助于类似Google Collections中的计算地图或Brian Goetz的书《Java并发性在实践中》中的记忆器示例。正如已经指出的,您需要解决线程安全问题。简单地使用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;
}
}
public enum LargeObjectCache{
实例;
私有最终ConcurrentMap=新ConcurrentHashMap();
公共大对象获取(字符串s){
大对象值=map.get(s);
如果(值==null){
值=新的较大对象;
LargeObject old=映射。putIfAbsent(s,值);
如果(旧!=null){
价值=旧;
}
}
返回值;
}
}
你需要在这个表单中准确地使用它,以获得正确和最有效的行为
如果您必须确保只有一个线程能够实例化给定键的值,那么就有必要求助于类似Google Collections中的计算地图或Brian Goetz的书《Java并发性实践》中的Memorizer示例。您说得对,谢谢。然而,在javadoc上,它提到了线程安全性:“这通常是通过在自然封装映射的某个对象上进行同步来实现的。如果不存在这样的对象,则应该使用Collections.synchronizedMap方法“包装”映射。”。那么,使用synchronized(map)和Collections.synchronizedMap(map)甚至使用ConcurrentHashMap有什么区别呢?它们似乎都有相同的目标,但我想一定有差异。如果您使用
synchronizedMap
(甚至是CuncurrentMap
),那么您可能会创建两次对象。在创建LargeObject
的过程中,您可以锁定特定的密钥(比如使用Future
),而不是锁定整个地图。但是,代码会变得更复杂。但是如果需要按需以原子方式创建实例,则putIfAbsend()的并发复合操作在大型对象上的成本非常高。如果使用synchronizedMap(),则可能存在一种竞争条件,即两个或多个线程锁定map.get(),多个线程执行map.put()每个操作都是线程安全的,但不是一对操作。你说得对,谢谢。然而,在javadoc上,它提到了线程安全性:“这通常是通过在自然封装映射的某个对象上进行同步来实现的。如果不存在这样的对象,则应该使用Collections.synchronizedMap方法“包装”映射。”。那么,使用synchronized(map)和Collections.synchronizedMap(map)甚至使用ConcurrentHashMap有什么区别呢?它们似乎都有相同的目标,但我想一定有差异。如果您使用synchronizedMap
(甚至是CuncurrentMap
),那么您可能会创建两次对象。在创建LargeObject
的过程中,您可以锁定特定的密钥(比如使用Future
),而不是锁定整个地图。但是,代码会变得更复杂。但是如果需要按需以原子方式创建实例,则putIfAbsend()的并发复合操作在大型对象上的成本非常高。如果使用synchronizedMap(),则可能存在一种竞争条件,即两个或多个线程锁定map.get(),多个线程执行map.put()每个操作都是线程安全的,但不是一对操作。