15

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 really necessary because synchronizedMap will always make sure that there are no two threads trying to do read/put operation on Map so why do we need to synchronize on that map itself?

Would really appreciate an explanation.


  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);
      }
    }
  }
}
Community
  • 1
  • 1
Rachel
  • 100,387
  • 116
  • 269
  • 365

2 Answers2

18

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<String> valuesList = new ArrayList<String>();
      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<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);
}

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.

Gray
  • 115,027
  • 24
  • 293
  • 354
  • 1
    my question is more of why we need to synchronize on `synchronizedMap`? – Rachel Jul 26 '12 at 14:44
  • 2
    @Rachel he just explained it didn't he? – Jon Taylor Jul 26 '12 at 14:47
  • so then question comes that what is the difference between `synchronizedMap` vs `Map`, I was under the impression that `synchronizedMap` would take care of protecting against race condition? – Rachel Jul 26 '12 at 14:49
  • 2
    As I say in my answer @Rachel, the `synchronizedMap` protects against _internal_ race conditions that would corrupt the map data itself. It can't protect against the example you posted and I annotated where multiple methods are being called. – Gray Jul 26 '12 at 14:52
  • @Gray: That's the part related to `synchronizedMap` protects against internal race condition that would corrupt map data is not clear to me, if possible, can you give an example with synchronizedMap and concurrentHashMap so that I can get better idea? – Rachel Jul 26 '12 at 14:56
  • 4
    I think the above example is perfect @Rachel. Without the `synchronized` wrapper, two threads could execute the `containsKey` line at the same time with the same `key`. They both test for existence of the key and they both see that the key is not in the map. Both of the threads create a new list and add their value to each of the lists. The first thread executes the put with its list and the second thread executes the put with its list, _overwriting_ the first thread's put. That is the race condition. Please see here: http://en.wikipedia.org/wiki/Race_conditions#Computing – Gray Jul 26 '12 at 15:01
  • @Gray: This could be dump question but as we have synchronizedMap will not only one thread be able to do operation on synchronizedMap like `if (synchronizedMap.containsKey(key))` ? – Rachel Jul 26 '12 at 15:05
  • @NathanHughes: Even if we have synchronizedMap, threads can interleave arbitrarily? I am under the impression that as we have synchronizedMap, only one would be playing around inside the Map? – Rachel Jul 26 '12 at 15:20
  • @Rachel, refresh your page and please go back and carefully read my answer. This is not about _inside_ consistency in the map. This is about _multiple_ calls to the map and how they interact in multiple threads. The collection is protected but your program logic is not. – Gray Jul 26 '12 at 15:24
  • ok, will it be right to say that synchronized collection would not protect against multiple threads adding values into it? – Rachel Jul 26 '12 at 15:31
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/14466/discussion-between-gray-and-rachel) – Gray Jul 26 '12 at 15:32
2

It is not just about updating synchronizedMap values, it is about sequence of operations effecting the map. There are two operations happening on the map inside same method.

If you don't synchronize block/method, assume there may be case like Thread1 executing first part and thread2 executing second part, your business operation may results in weird results (even though updates to map are synchronized)

kosa
  • 65,990
  • 13
  • 130
  • 167
  • but Collections.synchronizedMap(Map) synchronizes all operations (get, put, size, etc) so how would 2nd thread would ever do any kind of operation on Collections.synchronizedMap(Map)? – Rachel Jul 26 '12 at 15:10
  • You are correct, assume this scenario. You have a value in SynchMap let us say 10. In your code, the method is not synchronized, so assume T1 & T2 are in same block. We know SynchMap allows only one thread, so, let us assume T1 got first chance and executed contains() check and figured that what ever it is looking for is not there in map. – kosa Jul 26 '12 at 15:14
  • Then, in second part of code let us assume instead of T1, T2 got chance to execute the code and it adds whatever the value T1 was looking for. Because SynchMap, T1 will be on wait() until T2 done with the put() logic. – kosa Jul 26 '12 at 15:15
  • Now T1 turn comes into picture and tries to add same value because contains() check said map doesn't contain that value. But, technically in this scenario when T1 tries to put the value, it was already added by T2. – kosa Jul 26 '12 at 15:18
  • so what is challenge here and what benefit will i get from synchronized(synchronizedMap)? – Rachel Jul 26 '12 at 15:18
  • ok, now assume, same scenario with non-synch map. In that case if you observe properly, T1 don't need to wait for T2 put() operation completion. Both T1 & T2 tries to do put() operation at same time, which may lead to race condition (or) corrupt data. – kosa Jul 26 '12 at 15:19
  • Synch map atleast guarantee you what ever data it has is not corrupted due to modification from multiple threads at a time. – kosa Jul 26 '12 at 15:21
  • exactly my question is with `non synch map` vs `synchronizedMap` vs `synchronize(synchronizedMap)`, i agree with `non sync map` but am not able to understand why to use `synchronize(synchronizeMap)` when we can simply use `synchronizeMap`, hope am able to put my point across in apt manner? – Rachel Jul 26 '12 at 15:22
  • My last comment should answer it. If you use synch map, atleast you can guarantee that the data it has is valid & no race condition. Regarding synch (synchMap), may be let me tell you another example. Assume synchMap is bankacccount, T1 is You and T2 is Your Partner. You are at bank teller counter (both at diff tellers), doing transactions exact same time. – kosa Jul 26 '12 at 15:25
  • Let us say, first scenario, we DONT have synch(synchMap). assume 100$ in your account. Both want to withdraw 100$. – kosa Jul 26 '12 at 15:28
  • Let us say, first scenario, we DONT have synch(synchMap), We have just synchMap. assume 100$ in your account. Both want to withdraw 100$. Assume withdraw() method has checkbalance & withdraw. Both T1 & T2 enters withdraw method. Because sycnMap, only one thread can access, so let us assume T1 got first turn, it checks balance and yes we have sufficient amount. If T1 continues with withdraw logic, you don't see much issue. But assume, T1 is moved to wait() state and T2 got its turn. – kosa Jul 26 '12 at 15:35
  • Now T2 performs checkbalance, it says yes available. Then, let us assume T2 continued with withdraw logic, T2 gets the money and exits the method. Now T1 which is on wait state get its turn, and tries to withdraw money, but it is not available. – kosa Jul 26 '12 at 15:37
  • Take exact scenario with NON-SYNCH MAP. T1 & T2 competes for checkbalance (or) withdraw and may result in race condition. – kosa Jul 26 '12 at 15:38
  • Now second scenario, synch(synchMap). In this case, you will get two guarantees, one entire synch block will be executed by only one THREAD, so no other thread enters the block. – kosa Jul 26 '12 at 15:39
  • @Nambari I think this is a great example Nambari. But you said `But assume, T1 is moved to wait() state...`. Why would T1 all of a sudden go to `wait()` state? – czchlong Jun 26 '14 at 18:26
  • @czchlong: there could be multiple reasons for a thread to be in wait state, think link may help you :http://www.uml-diagrams.org/examples/java-6-thread-state-machine-diagram-example.html – kosa Jun 26 '14 at 18:56