使用ConcurrentHashMap,何时需要同步?

Per*_*ing 13 java concurrency concurrenthashmap

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

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

if(!sequences.containsKey(table)) {
    synchronized (sequences) {
        if(!sequences.containsKey(table))
            initializeHashMapKeyValue(table);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

if(!sequences.containsKey(table))
Run Code Online (Sandbox Code Playgroud)

检查synschronized块内部,以便其他线程不会初始化相同的hashmap值?

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

Joã*_*des 21

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

  • 你可以跳过额外检查的一种情况是当你不介意`initializeHashMapKeyValue(table)`对于相同的`table`值将被制作多一次.这可能是因为`initializeHashMapKeyValue`没有副作用而且相当便宜. (2认同)

Old*_*eon 17

你应该使用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;
}
Run Code Online (Sandbox Code Playgroud)

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

public long addTo(String key, long value) {
    return map.putIfAbsent(key, new AtomicLong()).addAndGet(value);
}
Run Code Online (Sandbox Code Playgroud)

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

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