Java HashMaps只有put and get - 可能是并发问题



我使用过ConcurrentHashMaps,但我不太确定这是否会涵盖这里的所有基础。

我有一个弹簧组件。此组件将包含一个映射。这只是外部服务中对象的快速参考。如果映射不包含匹配的字符串,它将调用外部服务,检索对象,并将其存储在映射中。然后,其他类可以使用映射进行快速检索和使用。因此,只有 put() 和 get() 操作在映射上执行。条目永远不会被删除。

话虽如此,我有点担心ConcurrentHashMap可能无法提供我想要的原子控制。从外部服务获取某个对象可能很昂贵。我宁愿不要让两个单独的线程几乎同时调用,从而导致对外部服务的相同值进行多次调用。

这个想法是这样的:

Map<String, SomeObject> map = Collections.concurrentHashMap(
    new HashMap<String, SomeObject>());
public SomeObject getSomeObject(String key){
    if (!map.containsKey(key)){
        map.put(key, retrieveSomeObjectFromService(key));
    }
    return map.get(key);

或者这个:

Map<String, SomeObject> map = new HashMap<String, SomeObject>();
public SomeObject getSomeObject(String key){
    synchronized(map){
        if (!map.containsKey(key)){
            map.put(key, retrieveSomeObjectFromService(key));
        }
    }
    return map.get(key);
}

前者当然更简单,但后者将确保一个两个或多个线程不会同时触发同一 SomeObject 的获取。或者,我想我可以尝试锁定仅尝试检索已经在获取过程中的 SomeObject,并且不会阻止检索已经存在的 SomeObjects,但这需要对各种字符串值的等待机制,我不确定如何最好地实现这一点。

我建议你两者兼而有之!

快速路径,只需 1 个退出并发哈希图。慢速路径,完全同步和锁定

private final ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<String, Object>();
private final ReentrantLock lock = new ReentrantLock();
public Object getSomeObject(String key) {
    Object value = map.get(key);
    if (value == null) {
        try {
            lock.lock();
            value = map.get(key);
            if (value == null) {
                value = retrieveSomeObjectFromService(key);
                map.put(key, value);
            }
        } finally {
            lock.unlock();
        }
    }
    return value;
}

你明白为什么我们需要第二个进入锁内吗? 省略这一点会留下一种情况,即我们最终制作内部对象两次,并使其的不同副本漂浮。

还使用包含方法将结果分配给值和 nullcheck 与 - 了解为什么这样更好? 如果我们做一个.contains,然后做一个.get,我们只做了2个哈希图查找。 如果我只是做一个get,我可以将我的哈希图查找时间减少一半。

彼得建议的另一个版本......更少的代码行,但不是我个人的偏好:

private final ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<String, Object>();
public Object getSomeObject(String key) {
    Object value = map.get(key);
    if (value == null) {
        synchronized (map) {
            value = map.get(key);
            if (value == null) {
                value = retrieveSomeObjectFromService(key);
                map.put(key, value);
            }
        }
    }
    return value;
}

最新更新