15

我指的是这里提出的问题并使用作者代码示例,现在我的问题是

  1. 为什么作者使用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);
      }
    }
  }
}
4

2 回答 2

18

为什么我们需要同步它synchronizemap本身?

您可能需要在已经同步的集合上进行同步,因为您正在对集合执行两个操作 - 在您的示例中,是 a containsKey(),然后是 a put()。您正在尝试防止调用集合的代码中出现竞争条件。此外,在这种情况下,块还保护这些值,以便多个线程可以将它们的值添加到这些不同步的集合中。synchronizedArrayList

如果您查看链接到的代码,他们首先检查键是否存在,然后如果键不存在则将值放入映射中。您需要防止 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块内。

于 2012-07-26T14:43:30.203 回答
2

这不仅仅是关于更新 synchronizedMap 值,它是关于影响地图的操作序列。在同一方法内的地图上发生了两个操作。

如果您不同步块/方法,假设可能存在 Thread1 执行第一部分和 thread2 执行第二部分的情况,您的业务操作可能会导致奇怪的结果(即使对 map 的更新是同步的)

于 2012-07-26T14:43:47.830 回答