Java synchronized block vs. Collections.synchronizedMap

后端 未结 7 1909
天涯浪人
天涯浪人 2020-11-28 03:35

Is the following code set up to correctly synchronize the calls on synchronizedMap?

public class MyClass {
  private static Map

        
7条回答
  •  再見小時候
    2020-11-28 04:09

    There is the potential for a subtle bug in your code.

    [UPDATE: Since he's using map.remove() this description isn't totally valid. I missed that fact the first time thru. :( Thanks to the question's author for pointing that out. I'm leaving the rest as is, but changed the lead statement to say there is potentially a bug.]

    In doWork() you get the List value from the Map in a thread-safe way. Afterward, however, you are accessing that list in an unsafe matter. For instance, one thread may be using the list in doWork() while another thread invokes synchronizedMap.get(key).add(value) in addToMap(). Those two access are not synchronized. The rule of thumb is that a collection's thread-safe guarantees don't extend to the keys or values they store.

    You could fix this by inserting a synchronized list into the map like

    List valuesList = new ArrayList();
    valuesList.add(value);
    synchronizedMap.put(key, Collections.synchronizedList(valuesList)); // sync'd list
    

    Alternatively you could synchronize on the map while you access the list in doWork():

      public void doWork(String key) {
        List values = null;
        while ((values = synchronizedMap.remove(key)) != null) {
          synchronized (synchronizedMap) {
              //do something with values
          }
        }
      }
    

    The last option will limit concurrency a bit, but is somewhat clearer IMO.

    Also, a quick note about ConcurrentHashMap. This is a really useful class, but is not always an appropriate replacement for synchronized HashMaps. Quoting from its Javadocs,

    This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details.

    In other words, putIfAbsent() is great for atomic inserts but does not guarantee other parts of the map won't change during that call; it guarantees only atomicity. In your sample program, you are relying on the synchronization details of (a synchronized) HashMap for things other than put()s.

    Last thing. :) This great quote from Java Concurrency in Practice always helps me in designing an debugging multi-threaded programs.

    For each mutable state variable that may be accessed by more than one thread, all accesses to that variable must be performed with the same lock held.

提交回复
热议问题