我指的是问here并使用作者代码示例,现在我的问题是
synchronized(synchronizedMap)
,这是否真的有必要,因为synchronizedMap将始终确保没有两个线程尝试对read/put
进行Map
操作,那么我们为什么需要该地图本身synchronize
? 真的很感激解释。
public class MyClass {
private static Map<String, List<String>> synchronizedMap =
Collections.synchronizedMap(new HashMap<String, List<String>>());
public void doWork(String key) {
List<String> values = null;
while ((values = synchronizedMap.remove(key)) != null) {
//do something with values
}
}
public static void addToMap(String key, String value) {
synchronized (synchronizedMap) {
if (synchronizedMap.containsKey(key)) {
synchronizedMap.get(key).add(value);
}
else {
List<String> valuesList = new ArrayList<String>();
valuesList.add(value);
synchronizedMap.put(key, valuesList);
}
}
}
}
答案 0 :(得分:18)
为什么我们需要同步
synchronizemap
本身?
您可能需要在已经同步的集合上进行同步,因为您要对集合执行两项操作 - 在您的示例中,containsKey()
然后是put()
。您正试图在调用集合的代码中防范race conditions。此外,在这种情况下,synchronized
块还保护ArrayList
值,以便多个线程可以将它们的值添加到这些未同步的集合中。
如果查看链接的代码,他们首先检查密钥是否存在,然后如果密钥不存在则将值放入映射中。您需要防止2个线程检查密钥是否存在,然后将两个放入映射中。比赛是一个人将首先放在哪一个,哪一个将覆盖之前的投注。
同步集合保护自己免受破坏地图本身的多个线程的影响。它不会 防止多次调用地图时的逻辑竞争条件。
synchronized (synchronizedMap) {
// test for a key in the map
if (synchronizedMap.containsKey(key)) {
synchronizedMap.get(key).add(value);
} else {
List<String> valuesList = new ArrayList<String>();
valuesList.add(value);
// store a value into the map
synchronizedMap.put(key, valuesList);
}
}
这是ConcurrentMap
接口具有putIfAbsent(K key, V value);
的原因之一。这样做不需要两个操作,因此您可能不需要围绕它进行同步。
synchronized (synchronizedMap) {
// test for a key in the map
List<String> valuesList = synchronizedMap.get(key);
if (valueList == null) {
valuesList = new ArrayList<String>();
// store a value into the map
synchronizedMap.put(key, valuesList);
}
valuesList.add(value);
}
最后,如果地图上的大多数操作无论如何都需要在synchronized
块中,那么您也可以不为synchronizedMap
支付费用,只需在HashMap
内使用synchronized
{{1}}块。
答案 1 :(得分:2)
它不仅仅是关于更新synchronizedMap值,而是关于影响地图的操作序列。在同一方法中,地图上发生了两个操作。
如果你不同步块/方法,假设可能有类似Thread1执行第一部分和thread2执行第二部分的情况,你的业务操作可能会导致奇怪的结果(即使地图的更新是同步的)