Difference between Hashtable, Collections.synchronizedMap and ConcurrentHashMap
Using HashMap in multi-threaded application can cause issues when multiple threads try to perform some modification operations on the same HashMap instance.
Synchronization of HashMap
Hashtable or Collections.synchronizedMap
ConcurrentHashMap
There are multiple ways by which a HashMap instance can be made synchronized and thread safe. The exact solution to this depends upon the actual requirement. However one can make HashMap thread safe by using the following guidelines:
-
Synchronization of HashMap
If you want to stick to HashMap only due to JDK limitations then you need to ensure that any modification to the HashMap is made only from a synchronized context.
You need to ensure that proper locking mechanism is in place when trying to add or remove elements from the same HashMap instance using multiple threads.
-
Hashtable or Collections.synchronizedMap
Collections.synchronizedMap(new HashMap()); is used to get a synchronized map wrapper around an earlier created HashMap. The Collections class is a helper class which provides a number of methods related to sorting and synchronization of underlying collection classes.
When we use synchronizedMap from Collections class, the operations on the returned wrapper get synchronized. The total effect is as if the methods of the underlying map have been marked as synchronized. The code for this method looks like:
private final Map<K,V> m; // Backing Map final Object mutex; // Object on which to synchronize SynchronizedMap(Map<K,V> m) { if (m==null) throw new NullPointerException(); this.m = m; mutex = this; } SynchronizedMap(Map<K,V> m, Object mutex) { this.m = m; this.mutex = mutex; } public int size() { synchronized (mutex) {return m.size();} } public boolean isEmpty() { synchronized (mutex) {return m.isEmpty();} } public boolean containsKey(Object key) { synchronized (mutex) {return m.containsKey(key);} } ................ ................Thus an object named as mutex is used as lock and any methods on the wrapper collection are in effect translated to calls on underlying map with additional synchronization using the mutex (which is a member reference variable)
While using the wrapper map returned by Collections.synchronizedMap method, make sure to synchronize multiple operations so that they become atomic operations.
For example:
If one thread is trying to add two key/value pair to synchronized map followed by a remove operation. Another thread is trying to remove two key/value pair followed by one add operation. Each add and remove operation is getting synchronized due the code inside the wrapper created by Collections class. But the three operations performed by first thread may get interleaved by the three operations from the second thread. This will result in unexpected results and can only be avoided by adding synchronization so that three operations in any of the thread become single operation.Moreover, it is advised by JDK to perform manual synchronization when accessing the elements of wrapper, for example the keyset for the wrapper map object. The synchronization level provided by synchronizedMap wrapper is same as that provided by Hashtable class.
Due to the involvement of additional synchronization, there is performance degradation seen when using the wrapper map in multi-threaded applications. Apart from synchronized map wrapper implementation, Collections class can also provide synchronized wrapper for ArrayList. So Collections class is not limited to hashcode based collections only.
-
ConcurrentHashMap
The performance issues described in the use of Collections.synchronizedMap can be addressed by the use ConcurrentHashMap class. The ConcurrentHashMap class uses a number of locks to add synchronization to the map operations.
Some salient features about ConcurrentHashMap which can help understand the working of ConcurrentHashMap in details are:
a) This class doesn’t lock the read operations. Only add and remove operations are locked.
b) The use of this class is different from Collections.synchronizedMap is that former uses multiple locks (maximum of 32) for write operations where as the latter locks the whole map. This means that 32 different threads could be modifying the same ConcurrentHashMap at a single point of time where as only one thread can modify Hashtable or Collections.synchronizedMap.Thus, if performance is on your mind then do use ConcurrentHashMap instead if Collections.synchronizedMap.





