最小的阻塞缓存存储

时间:2015-03-09 16:07:50

标签: java performance concurrency latency

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

method1(Arg arg)
method2(Arg arg)

当我们进行特定调用时,我们有一个响应的哈希值,以便我们可以将它们放在地图中...

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

现在我们下次获得响应时,我们从该缓存存储中检索此特定“哈希”并进行比较...

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

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

我想知道是否已经为此目的使用了lib(缓存)?

如果没有,那么是否有任何Map实现允许按键进入?或者我会保留另一张地图?

通常使用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;
    }
  }
}

如果你看到可能的错误,请评论:)

1 个答案:

答案 0 :(得分:2)

我认为你对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块之前为同一个键设置了值。