Why to synchronize on SynchronizedMap or SynchronizedCollections?

前端 未结 2 1399
耶瑟儿~
耶瑟儿~ 2021-02-02 16:24

I am referring to question asked here and using authors code example, now my question is

  1. Why does author uses synchronized(synchronizedMap), is it re
2条回答
  •  鱼传尺愫
    2021-02-02 16:33

    why do we need to synchronize on that synchronizemap itself?

    You may need to synchronize on an already synchronized collection because you are performing two operations on the collection -- in your example, a containsKey() and then a put(). You are trying to protect against race conditions in the code that is calling the collection. In addition, in this case, the synchronized block also protects the ArrayList values so that multiple threads can add their values to these unsynchronized collections.

    If you look at the code you linked to, they first check for the existence of the key and then put a value into the map if the key did not exist. You need to protect against 2 threads checking for a key's existence and then both of them putting into the map. The race is which one will put first and which one will overwrite the previous put.

    The synchronized collection protects itself from multiple threads corrupting the map itself. It does not protect against logic race conditions around multiple calls to the map.

    synchronized (synchronizedMap) {
        // test for a key in the map
        if (synchronizedMap.containsKey(key)) {
          synchronizedMap.get(key).add(value);
        } else {
          List valuesList = new ArrayList();
          valuesList.add(value);
          // store a value into the map
          synchronizedMap.put(key, valuesList);
       }
    }
    

    This is one of the reasons why the ConcurrentMap interface has the putIfAbsent(K key, V value);. That does not require two operations so you may not need to synchronize around it.

    Btw, I would rewrite the above code to be:

    synchronized (synchronizedMap) {
        // test for a key in the map
        List valuesList = synchronizedMap.get(key);
        if (valueList == null) {
          valuesList = new ArrayList();
          // store a value into the map
          synchronizedMap.put(key, valuesList);
        }
        valuesList.add(value);
    }
    

    Lastly, if most of the operations on the map need to be in a synchronized block anyway, you might as well not pay for the synchronizedMap and just use a HashMap always inside of synchronized blocks.

提交回复
热议问题