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

  1.  ConcurrentHashMap() – This constructor creates an empty ConcurrentHashMap with initial capacity (16), load factor (0.75) and concurrency level (16).
  2. new ConcurrentHashMap(int initialCapacity) – This constructor creates an empty ConcurrentHashMap with the specified initial capacity, and load factor (0.75) and concurrency level (16).
  3. ConcurrentHashMap(int initialCapacity, float loadFactor) – This constructor creates an empty ConcurrentHashMap with the specified initial capacity, specified load factor and default concurrency level (16).
  4. ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) – This constructor creates an empty ConcurrentHashMap with the specified initial capacity, load factor, and concurrency level.
  5. 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

 

Related Articles

post a comment