1

有没有更好的方法来缓存一些非常大的对象,这些对象只能创建一次,因此需要缓存?目前,我有以下内容:

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 传递给每个使用它的类。

此外,该映射不包含很多键(一个或两个,但键在程序的不同运行中可能会有所不同),那么在这种情况下是否还有另一个更有效的映射?

4

2 回答 2

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;
    }
}
于 2009-07-19T12:02:59.723 回答
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 示例。

于 2009-07-19T14:10:43.053 回答