为什么要同步SynchronizedMap或SynchronizedCollections?

时间:2012-07-26 14:41:10

标签: java multithreading

我指的是问here并使用作者代码示例,现在我的问题是

  1. 为什么作者使用synchronized(synchronizedMap),这是否真的有必要,因为synchronizedMap将始终确保没有两个线程尝试对read/put进行Map操作,那么我们为什么需要该地图本身synchronize
  2. 真的很感激解释。


      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);
          }
        }
      }
    }
    

2 个答案:

答案 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执行第二部分的情况,你的业务操作可能会导致奇怪的结果(即使地图的更新是同步的)