最小阻塞缓存存储

Minimal blocking cache store

假设我们有不同的方法来执行一些 http 调用,每个方法都用一些特定的参数调用...我们想比较方法 + 参数的最后一个值,看看响应是否不同,然后才继续。 ..

method1(Arg arg)
method2(Arg arg)

当我们进行特定调用时,我们有响应的哈希值,以便我们可以将它们放入映射中...

{"key" : "method1|arg", "value" : "hash"}

现在,下次我们收到响应时,我们会从该缓存存储中检索这个特定的 "hash" 并进行比较...

但是所有方法|arg 调用都是并发的,并且可能有许多相同组合的调用并行 运行,只有并发问题可能发生在入门级别...当相同的调用尝试时在另一个正在更新时更新缓存或读取...

所以我们需要在一个入口对象上同步,这样我们就只有 "method|arg" 的唯一完全相同的组合才能阻止它......只有相同的调用才能阻止它的其他执行,并且不会阻止与它无关的其他调用。

我想知道是否有用于此目的的库(缓存)?

如果没有,那么是否有任何 Map 实现可以通过 key 获取 Entry?或者我再保留一张地图?

通常使用 HashMap 并在 Entry 对象上同步是否安全? (我真的无法想象当 HashMap 重新散列并且一些并发获取正在执行时会发生什么......)

更新

这是我想出的实现...尽管 ConcurrentHashMap 可能涵盖了这种情况,但我的想法是只锁定一个条目而不是整个地图...(写入除外)

public class HashCache {
  final HashMap<String, Holder> hashCache = new HashMap<>();

  public boolean hasChanged(String key, Object hash) {
    assert key != null && hash != null;

    Holder holder = hashCache.get(key);

    if (holder == null) {
      synchronized (hashCache) {
        hashCache.put(key, new Holder(hash));
      }
      return true; // first hash
    } else {
      synchronized (holder) {
        if (Objects.equals(holder.object, hash)) {
          return false; // hash not changed
        } else {
          holder.object = hash;
          return true; // hash changed
        }
      }
    }
  }

  private static class Holder {
    Object object;

    Holder(Object object) {
      this.object = object;
    }
  }
}

如果您发现可能存在的错误,请发表评论:)

我想你可以 ConcurrentHashMap。我认为您不需要缓存,因为您不需要缓存响应,而是存储响应的哈希值。

ConcurrentHashMap 是一个高度优化的 Map,它尽可能避免线程争用,尤其是对于读取(我相信这符合您的情况)。

您可以使用另一种方法并在从公共 HashMap 获取每个条目后锁定它,但我认为这样做不值得。我会先使用 ConcurrentHashMap 并对其进行测试,并且只会在行为与预期结果不同时更改实现。

编辑:

根据您的编辑,我必须坚持建议您使用 ConcurrentHashMap。无论如何,如果出于某种原因这对您来说负担不起,我相信您在第一次将值放入地图时应该仔细检查:

public boolean hasChanged(String key, Object hash) {
    assert key != null && hash != null;

    Holder holder = hashCache.get(key); 
    if (holder == null) {
        synchronized (hashCache) { // Double-check that value hasn't been changed 
                                   // before entering synchronized block 
            holder = hashCache.get(key);
            if (holder == null) { 
                hashCache.put(key, new Holder(hash));
                return true; // first hash
            } // inner if
        }  // sync block
    } // outer if

    // No more else!

    synchronized (holder) {
        if (Objects.equals(holder.object, hash)) {
            return false; // hash not changed
        } else {
            holder.object = hash;
            return true; // hash changed
        }
    }
}

需要仔细检查,因为另一个线程可能在您的第一个 get() 之后但在您进入 synchronized 块之前为同一键设置了一个值。