使用 ConcurrentHashMap,何时需要同步?

2022-09-03 08:57:31

我有一个 ConcurrentHashMap,我在其中执行以下操作:

sequences = new ConcurrentHashMap<Class<?>, AtomicLong>();

if(!sequences.containsKey(table)) {
    synchronized (sequences) {
        if(!sequences.containsKey(table))
            initializeHashMapKeyValue(table);
    }
}

我的问题是 - 是否没有必要做额外的

if(!sequences.containsKey(table))

检查同步块内部,以便其他线程不会初始化相同的哈希映射值?

也许检查是必要的,我做错了?我正在做的事情似乎有点愚蠢,但我认为这是必要的。


答案 1

ConcurrentHashMap 上的所有操作都是线程安全的,但线程安全的操作是不可组合的。你试图使原子一对操作:检查地图中的东西,如果它不在那里,把一些东西放在那里(我假设)。所以你的问题的答案是肯定的,你需要再次检查,你的代码看起来还行。


答案 2

您应该使用 的 putIfAbsent 方法。ConcurrentMap

ConcurrentMap<String, AtomicLong> map = new ConcurrentHashMap<String, AtomicLong> ();

public long addTo(String key, long value) {
  // The final value it became.
  long result = value;
  // Make a new one to put in the map.
  AtomicLong newValue = new AtomicLong(value);
  // Insert my new one or get me the old one.
  AtomicLong oldValue = map.putIfAbsent(key, newValue);
  // Was it already there? Note the deliberate use of '!='.
  if ( oldValue != newValue ) {
    // Update it.
    result = oldValue.addAndGet(value);
  }
  return result;
}

对于我们当中的功能纯粹主义者来说,上述内容可以简化(或者可能复杂化)为:

public long addTo(String key, long value) {
    return map.putIfAbsent(key, new AtomicLong()).addAndGet(value);
}

在Java 8中,我们可以避免不必要的创建:AtomicLong

public long addTo8(String key, long value) {
    return map.computeIfAbsent(key, k -> new AtomicLong()).addAndGet(value);
}

推荐