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保存,因此我不能用队列或其他东西替换地图.
有任何想法吗?谢谢!
您可以利用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)