简体   繁体   中英

Extending HashMap<K,V> and synchronizing only puts

I recently encountered a class on our code base that extends HashMap and synchronizes the put method.

Aside from it being less efficient than using ConcurrentHashMap, what sort of problems may arise with extending HashMap and synchronizing only put(K,V)?

Assume that we don't care whether get(K) returns the latest value or not(eg we are fine with threads overwriting each other and we don't care about the possible race conditions that may arise if the values of the map are used as locks themselves).

Example:

public class MyMap<K,V> extends HashMap<K,V> {
  //...
   public synchronized void put(K key, V value) {
      //...
   }

  //...
}

As far as I know, HashMap does its re-size with the put method and since the put is synchronized at the map instance level, the problems encountered during concurrent re-sizing will(probably) not be encountered.

Even with the questionable assumptions above, my gut-feeling is telling me that there are more problems that may arise. Or am I just being paranoid?

Update: Thank you all, that was fun and enlightening. If I ever meet the original of author of this particular class, I can now explain his folly in detail. :)

In summary: putAll can still screw up the data structure horribly and end up in the dreaded infinite-loop/data-race condition. get relies on the underlying internal data structures of hashmap that may be in the process of being modified concurrently causing the get process to behave weirdly. This is just a generally bad idea. At the very least, the author could have used Collections.synchronizedMap(Map) instead.

Note: All three answers given as of this writing are actually correct but I picked the one about get() as the correct answer since it was the least obvious one for me.

I hope you are also synchronizing on putAll and remove . putAll especially since multiple threads can try and resize your HashMap. Those methods too will be updating size and modCount which if done outside of synchronization can result in lost updates.

since get() can be reading a changing data structure, everything bad can happen.

I've seen get() trapped in dead loop, so it's not just a theoretical possibility, bad things do happen.

As I mentioned in the comments, another problem that can arise is that the putAll(Map) method does not appear to be synchronized. Since putAll can also modify the structure of the Map, it is unsafe to call it unsynchronized from one thread while another thread is using the same Map.

At a higher-level though, it'd be interesting to understand more of the why around why put(key, value) was synchronized . Even if you've now guarded against unsynchronized modifications to the map's structure, it still does not seem like a good idea for multiple threads to be accessing the map without synchronization. In fact, if thread A is attempting to iterate over the HashMap's contents, and thread B calls synchronized put(key, value) , the iterator in Thread A will still fail fast and throw a ConcurrentModificationException rather than do something non-deterministic.

Even if you were to synchronize the putAll(Map) call, other threads iterating over the map's contents will still see exceptions. If the Map needs to be used in multiple threads and at least one of those threads needs to modify the Map, all of the calls need to be synchronized, period.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM