Java ConcurrentHashMap
ConcurrentHashMap is a class introduced in Java 1.5 Version which implements the ConcurrentMap interface. The ConcurrentHashMap is simply a concurrent version of the HashMap, the functionality is also similar to that of a HashMap except for the internal concurrency. In this article, we will get to know what is Java ConcurrentHashMap and it’s usage, along with it we will also have a look at the difference between HashMap and ConcurrentHashMap and difference between ConcurrentHashMap, SynchronizedMap, and HashTable.
Why we need ConcurrentHashMap?
Whenever we say Map, we already know two popular implementations HashMap and HashTable? Then Why we need ConcurrentHashMap?
Along with this, there might rise a lot of questions like?
If HashMap is not considered as ThreadSafe, then we can simply make it Synchronized using the Collections.synchronizedMap() method. Even if this approach didn’t work, then we have HashTable which is by default ThreadSafe. Then, What is the additional feature that a ConcurrentHashMap provides?
The problem with SynchronizedMap and HashTable is that it locks the entire object, so only one thread will be able to access the Map object even for the read operation, whereas ConcurrentHashMap uses a different type of locking mechanism which allows multiple threads to read and write concurrently without compromising the Thread Safety.
Java ConcurrentHashMap
How ConcurrentHashMap works internally?
We all know that ConcurrentHashMap functions exactly like a HashMap, but defers on the locking mechanism.
In order to understand it better, let’s recall the internal implementation of HashMap. The HashMap stores the values in buckets and there are 16 buckets by default.
ConcurrentHashMap calls each bucket as Segment and provides a separate lock for each Segment and hence the default concurrency level is also 16. In the ConcurrentHashMap code itself, we could see there are two constants defined.
static final int DEFAULT_INITIAL_CAPACITY = 16;
static final int DEFAULT_CONCURRENCY_LEVEL = 16;
So whenever a thread needs to do some update operation on the Map, it need not acquire the lock on the entire object all it needs to do is just get the lock for the particular segment alone. Since there 16 segment locks available, at any point in time 16 threads can concurrently perform the update operation.
Furthermore, the thread doesn’t need any sort lock in order to perform read operation on the ConcurrentHashMap, so in simple words, we can say any number of threads can perform read operation and 16 threads can perform update operation simultaneously at a given time.
Constructors on ConcurrentHashMap
- ConcurrentHashMap() – This constructor creates an empty ConcurrentHashMap with initial capacity (16), load factor (0.75) and concurrency level (16).
- new ConcurrentHashMap(int initialCapacity) – This constructor creates an empty ConcurrentHashMap with the specified initial capacity, and load factor (0.75) and concurrency level (16).
- ConcurrentHashMap(int initialCapacity, float loadFactor) – This constructor creates an empty ConcurrentHashMap with the specified initial capacity, specified load factor and default concurrency level (16).
- ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) – This constructor creates an empty ConcurrentHashMap with the specified initial capacity, load factor, and concurrency level.
- ConcurrentHashMap(Map<? extends K,? extends V> m) – This constructor creates a ConcurrentHashMap from the existing map which is passed to it.
Java ConcurrentHashMap Example
Let’s take a look into a simple Java ConcurrentHashMap example, we will also see some of the new methods such as putIfAbsent(), remove(), replace() which are added to the ConcurrentMap interface.
package com.javainterviewpoint.concurrenthashmap; import java.util.concurrent.ConcurrentHashMap; public class ConcurrentHashMapExample { public static void main(String[] args) { ConcurrentHashMap<Integer, String> chm = new ConcurrentHashMap<Integer, String>(); chm.put(101, "Jim"); chm.put(102, "Tim"); chm.putIfAbsent(103, "Tom"); chm.putIfAbsent(104, "Jerry"); chm.putIfAbsent(105, "Nick"); /** Newly added in ConcurrentMap interface, Wont be added because there is any entry already exist for 102 **/ chm.putIfAbsent(102, "Timmy"); /** Newly added in ConcurrentMap interface, removes the entry only when both key and value matches Nothing will happen, though key matches value doesn't match **/ chm.remove(105, "Jip"); System.out.println(chm); // Removes 104 entity chm.remove(104,"Jerry"); System.out.println(chm); // Replaces Nick with the value JIP chm.replace(105, "Nick", "JIP"); System.out.println(chm); } }
null not allowed in ConcurrentHashMap
Even though one null key and multiple null values are allowed in HashMap, ConcurrentHashMap doesn’t allow either null key or null value.
package com.javainterviewpoint.concurrenthashmap; import java.util.concurrent.ConcurrentHashMap; public class ConcurrentHashMapExample { public static void main(String[] args) { ConcurrentHashMap<Integer, String> chm = new ConcurrentHashMap<Integer, String>(); chm.put(101, "Jim"); chm.put(102, "Tim"); chm.putIfAbsent(103, "Tom"); chm.putIfAbsent(104, "Jerry"); chm.putIfAbsent(105, "Nick"); chm.put(null, "James"); System.out.println(chm); } }
Since in ConcurrentHashMap multiple threads will do modifications to the Map there might be a possibility that key k might be deleted in between the containsKey(k) and get(k) calls.
If we try to add a null key, we will get NullPointerException.
Exception in thread "main" java.lang.NullPointerException at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011) at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006) at com.javainterviewpoint.concurrenthashmap.ConcurrentHashMapExample.main(ConcurrentHashMapExample.java:16)
No ConcurrentModificationException / Fail-Safe iterator
The iterator of ConcurrentHashMap is fail-safe, which means that the iterator will not throw ConcurrentModificationException when the underlying collection is modified during iteration.
When we try to add a new entity to the HashMap while iterating we will be getting ConcurrentModificationException
package com.javainterviewpoint.concurrenthashmap; import java.util.HashMap; import java.util.Iterator; public class HashMapExample { public static void main(String[] args) { HashMap<Integer, String> hm = new HashMap<Integer, String>(); hm.put(1, "One"); hm.put(2, "Two"); hm.putIfAbsent(3, "Three"); hm.putIfAbsent(4, "Four"); hm.putIfAbsent(5, "Five"); Iterator it = hm.keySet().iterator(); while(it.hasNext()) { Integer key = (Integer) it.next(); System.out.println("Key: "+key+" Value: "+hm.get(key)); if(key == 3) { hm.put(6,"Six"); } } System.out.println(hm); } }
Output:
Key: 1 Value: One Key: 2 Value: Two Key: 3 Value: Three Exception in thread "main" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445) at java.util.HashMap$KeyIterator.next(HashMap.java:1469) at com.javainterviewpoint.concurrenthashmap.HashMapExample.main(HashMapExample.java:22)
Whereas in the case of ConcurrentHashMap we will not ConcurrentModificationException, Let’s change the above code to ConcurrentHashMap
package com.javainterviewpoint.concurrenthashmap; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; public class ConcurrentHashMapExample { public static void main(String[] args) { ConcurrentHashMap<Integer, String> chm = new ConcurrentHashMap<Integer, String>(); chm.put(1, "One"); chm.put(2, "Two"); chm.putIfAbsent(3, "Three"); chm.putIfAbsent(4, "Four"); chm.putIfAbsent(5, "Five"); Iterator it = chm.keySet().iterator(); while(it.hasNext()) { Integer key = (Integer) it.next(); System.out.println("Key: "+key+" Value: "+chm.get(key)); if(key == 3) { chm.put(6,"Six"); } } System.out.println(chm); } }
Output:
Key: 1 Value: One Key: 2 Value: Two Key: 3 Value: Three Key: 4 Value: Four Key: 5 Value: Five Key: 6 Value: Six {1=One, 2=Tow, 3=Three, 4=Four, 5=Five, 6=Six}
Difference between HashMap and ConcurrentHashMap
HashMap | ConcurrentHashMap |
---|---|
HashMap is not Synchronized | ConcurrentHashMap is Synchronized |
HashMap is not Thread Safe | ConcurrentHashMap is Thread Safe |
In HashMap 1 null key and multiple null values are allowed | ConcurrentHashMap does not allow either null key or a null value if we try to add we will get NullPointerException |
During iteration, when the underlying HashMap is modified, we will get ConcurrentModificationException | During iteration, we can make modifications to underlying ConcurrentHashMap, we will not get ConcurrentModificationException |
The Iterator of HashMap is Fail-Fast | The Iterator of ConcurrentHashMap is Fail-Safe |
Performance of HashMap is comparatively higher than ConcurrentHashMap as HashMap is not Thread Safe | Performance of ConcurrentHashMap is comparatively lower than HashMap, as ConcurrentHashMap is Thread Safe |
Introduced in 1.2 Version of Java | Introduced in 1.5 Version of Java |
Difference between – ConcurrentHashMap vs SynchronizedMap vs HashTable
ConcurrentHashMap | SynchronizedMap [Collections.synchronizedMap()] | HashTable |
---|---|---|
We will get thread safety without locking the entire Map object, just Segment / Bucket level lock is enough | We will get thread safety by locking the complete Map Object | We will get thread safety by locking the complete Map Object |
At a time multiple threads are allowed to perform any operation on the Map object | At a time only one thread is allowed to perform any operation on the Map object | At a time only one thread is allowed to perform any operation on the Map object |
Read operation can be performed without a lock and Write operation can be performed with bucket/segment level lock | Both Read and Write operation requires the lock on the complete Map object | Both Read and Write operation requires the lock on the complete Map object |
During iteration, we are allowed to make a modification to the underlying ConcurrentHashMap and we will not get ConcurrentModificationException | During iteration, we are allowed to make a modification to the underlying SynchronizedMap and we will get ConcurrentModificationException | During iteration, we are allowed to make a modification to the underlying HashTable and we will get ConcurrentModificationException |
Performance is comparatively high when compared with SynchronizedMap and HashTable because of the bucket level locking mechanism | Performance is comparatively low when compared with ConcurrentHashMap because of whole Map object lock | Performance is comparatively low when compared with ConcurrentHashMap because of whole Map object lock |
The Iterator of ConcurrentHashMap is Fail-Safe, that is during iteration when the underlying collection is modified we will not get ConcurrentModificationException | The Iterator of SynchronizedMap is Fail-Fast, that is during iteration when the underlying collection is modified we will get ConcurrentModificationException | The Iterator of HashTable is Fail-Fast, that is during iteration when the underlying collection is modified we will get ConcurrentModificationException |
For both key and value null is not allowed | 1 null key and multiple null values are allowed | For both key and value null is not allowed |
Introduced in 1.5 Version of Java | Introduced in 1.2 Version of Java | Introduced in 1.0 Version of Java |
post a comment