【发布时间】:2016-04-14 21:12:50
【问题描述】:
1.
我有多个线程更新 ConcurrentHashMap。 每个线程根据键将整数列表附加到映射条目的值。 没有任何线程的删除操作。
这里的重点是我想尽可能减少锁定和同步的范围。
我看到 computeIf...() 方法的文档说“在计算过程中,其他线程在此映射上的一些尝试更新操作可能会被阻止”,这并不令人鼓舞。另一方面,当我查看它的源代码时,我没有观察到它在整个地图上锁定/同步的位置。
因此,我想知道使用 computeIf...() 和以下本土“方法 2”的理论性能比较。
2.
另外,我觉得我在这里描述的问题可能是您可以在 ConcurrentHashMap 上执行的最简化的 check-n-set(或通常是“复合”)操作之一。
但我不太自信,找不到太多指导关于如何在 ConcurrentHashMap 上执行这种简单的复合操作,无需在整个地图上锁定/同步。
因此,我们将不胜感激任何一般性的良好实践建议。
public void myConcurrentHashMapTest1() {
ConcurrentHashMap<String, List<Integer>> myMap = new ConcurrentHashMap<String, List<Integer>>();
// MAP KEY: a Word found by a thread on a page of a book
String myKey = "word1";
// -- Method 1:
// Step 1.1 first, try to use computeIfPresent(). doc says it may lock the
// entire myMap.
myMap.computeIfPresent(myKey, (key,val) -> val.addAll(getMyVals()));
// Step 1.2 then use computeIfAbsent(). Again, doc says it may lock the
// entire myMap.
myMap.computeIfAbsent(myKey, key -> getMyVals());
}
public void myConcurrentHashMapTest2() {
// -- Method 2: home-grown lock splitting (kind of). Will it theoretically
// perform better?
// Step 2.1: TRY to directly put an empty list for the key
// This may have no effect if the key is already present in the map
List<Integer> myEmptyList = new ArrayList<Integer>();
myMap.putIfAbsent(myKey, myEmptyList);
// Step 2.2: By now, we should have the key present in the map
// ASSUMPTION: no thread does removal
List<Integer> listInMap = myMap.get(myKey);
// Step 2.3: Synchronize on that list, append all the values
synchronized(listInMap){
listInMap.addAll(getMyVals());
}
}
public List<Integer> getMyVals(){
// MAP VALUE: e.g. Page Indices where word is found (by a thread)
List<Integer> myValList = new ArrayList<Integer>();
myValList.add(1);
myValList.add(2);
return myValList;
}
【问题讨论】:
-
一般来说,它不会锁定整个地图,而只是地图的segment。
-
锁定是悲观的,而不是乐观的,因此您应该使用 get 预先筛选这两种方法。
-
@LouisWasserman 谢谢!如果你也在谈论 JDK8,例如computeIfAbsent(),我假设你指的是 for {... else{...synchronized (f)...}} 中的内容?
-
@BenManes 谢谢!但抱歉,当你说“你应该用 get 预先筛选这两种方法”时,我不太明白
-
@Stochastika “读取,如果不存在,则计算如果不存在”避免锁定 bin 以确定条目是否存在。相反,大多数读取将在未找到和计算时找到具有少量锁定的现有值。这个可以大大improve performance。
标签: java multithreading java-8 concurrenthashmap atomic