Java:get + clear atomic for map

luc*_*uta 6 java collections concurrency

我想实现以下逻辑:

- 使用以下结构

//Map<String, CopyOnWriteArrayList> keeping the pending updates 
//grouped by the id of the updated object
final Map<String, List<Update>> updatesPerId = new ConcurrentHashMap<>();
Run Code Online (Sandbox Code Playgroud)

-n producer将为updatesPerId map添加更新(对于相同的id,可以同时添加2个更新)

-one TimerThread将不时运行,并且必须处理收到的更新.就像是:

 final Map<String, List<Update>> toBeProcessed = new HashMap<>(updatesPerId);
 updatesPerId.clear();
 // iterate over toBeProcessed and process them
Run Code Online (Sandbox Code Playgroud)

有没有办法在不同步生产者的添加逻辑和timerThread(消费者)的逻辑的情况下使这个逻辑线程安全?我正在考虑一个原子清除+ get但似乎ConcurrentMap没有提供类似的东西.另外,我必须提到更新应该由更新的对象ID保存,因此我不能用队列或其他东西替换地图.

有任何想法吗?谢谢!

And*_*ner 5

您可以利用ConcurrentHashMap.compute原子执行的事实。

你可以updatesPerId像这样:

updatesPerId.compute(k, (k, list) -> {
  if (list == null) list = new ArrayList<>();
  // ... add to the list

  // Return a non-null list, so the key/value pair is stored in the map.
  return list;
});
Run Code Online (Sandbox Code Playgroud)

不是使用computeIfAbsentthen 添加到返回值,这不是原子的。

然后在你的线程中删除东西:

for (String key : updatesPerId.keySet()) {
  List<Update> list = updatesPerId.put(key, null);
  updatesPerId.compute(key, (k, list) -> {
    // ... Process the contents of the list.

    // Removes the key/value pair from the map.
    return null;
  });
}
Run Code Online (Sandbox Code Playgroud)

因此,如果您碰巧尝试同时处理两个位置的键,则向列表中添加键(或处理该键的所有值)可能会阻塞;否则,它不会被阻止。


编辑:正如@StuartMarks 所指出的,最好先从地图中取出所有内容,然后再处理它们,以避免阻塞其他线程尝试添加:

Map<String, List<Update>> newMap = new HashMap<>();
for (String key : updatesPerId.keySet()) {
  newMap.put(key, updatesPerId.remove(key));
}
// ... Process entries in newMap.
Run Code Online (Sandbox Code Playgroud)