为什么要在SyncedMap或SyncedCollections上进行同步?

2022-09-03 13:56:58

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

  1. 为什么作者使用,这真的是必要的,因为syncedMap将始终确保没有两个线程试图做操作,那么为什么我们需要在那个地图本身上?synchronized(synchronizedMap)read/putMapsynchronize

真的非常感谢解释。


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

答案 1

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

您可能需要在已同步的集合上进行同步,因为您正在对该集合执行两个操作 - 在您的示例中,a 和 .您正在尝试防止调用集合的代码中的争用条件。此外,在这种情况下,该块还会保护这些值,以便多个线程可以将其值添加到这些不同步的集合中。containsKey()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);
   }
}

这是接口具有 .这不需要两个操作,因此您可能不需要围绕它进行同步。ConcurrentMapputIfAbsent(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);
}

最后,如果地图上的大多数操作无论如何都需要在一个块中,那么你最好不要付钱,而只使用一个总是在块内。synchronizedsynchronizedMapHashMapsynchronized


答案 2

这不仅仅是更新同步的Map值,而是影响映射的操作序列。在同一方法内,地图上发生了两个操作。

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