Java 5 – ConcurrentHashMap with Read and Update operations simultaneously

In this article, we will discuss how can we achieve both read (iterate) and modify (remove/add) operations simultaneously by 2 different threads using ConcurrentHashMap which isn’t possible with simple HashMap

1. HashMap:

If 2 different threads perform operations on same HashMap object simultaneously, then compiler will throw ConcurrentModificationException

We will demonstrate a simple example using HashMap which performs

  • 1st thread iterating or reading entries one-by-one
  • 2nd thread removing key-value pair; while other thread is iterating HashMap object

IterateAndModifyHashMapSimultaneously.java

package in.bench.resources.concurrent.collection;

import java.util.HashMap;
import java.util.Map;

// extending Thread class
public class IterateAndModifyHashMap extends Thread {

	// creating HashMap object of type <Integer, String>
	static HashMap<Integer, String> hm =
			new HashMap<Integer, String>();

	@Override
	public void run() {

		try {
			// sleeping thread for 1000 ms
			Thread.sleep(1000);

			// removing entry with key=1
			String value = hm.remove(1);
			System.out.println("Entry with {key=1" +
					" and value=" + value + "} is removed");
		}
		catch(InterruptedException iex) {
			iex.printStackTrace();
		}
		System.out.println("Removal is done... !!");
	}

	/**
	 * main() method
	 * @param args
	 * @throws InterruptedException
	 */
	public static void main(String[] args) throws InterruptedException {

		// adding key-value pairs to HashMap object
		hm.put(1, "google.com");
		hm.put(2, "youtube.com");
		hm.put(3, "facebook.com");

		// creating another thread
		Thread newThread = new Thread(new IterateAndModifyHashMap());
		newThread.start();

		// iterating HM object using enhanced for-loop
		for(Map.Entry<Integer, String> me : hm.entrySet()) {

			System.out.println("{Key=" + me.getKey()
					+ "\t" + "Value=" + me.getValue() + "}");

			// sleeping thread for 1500 ms, after every turn
			Thread.sleep(1500);
		}
		System.out.println("Iterating completed... !!");
	}
}

Output:

{Key=1	Value=google.com}
Entry with {key=1 and value=google.com} is removed
Removal is done... !!
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextEntry(HashMap.java:895)
	at java.util.HashMap$EntryIterator.next(HashMap.java:935)
	at java.util.HashMap$EntryIterator.next(HashMap.java:933)
	at in.bench.resources.concurrent.collection
        .IterateAndModifyHashMap.main(IterateAndModifyHashMap.java:48)

Explanation:

  • Main thread iterating HashMap object and child thread removing HashMap entry with key=1
  • From output, it is clear that while one thread is iterating on HashMap object and if any other thread perform modify operation (i.e.; on the same HashMap object other thread is removing an entry)
  • Then compiler will throw ConcurrentModificationException
  • Note:sleep(ms) introduced to study the demo example
  • Because without sleep both thread will execute independently (complete its execution in nano/pico seconds) and there won’t be any compile-time error
  • Since we are trying to understand with small amount of data (where execution completes in nano seconds)
  • But with large set of data, introduction of sleep concept isn’t required
  • As execution time increases for each thread, definitely ConcurrentModificationException is thrown

Q) How to overcome above exception with HashMap ?

  • With ConcurrentHashMap, we can overcome this problem
  • as it works on different locking strategy or different concurrency level

2. ConcurrentHashMap:

When 2 different threads performs operations on same ConcurrentHashMap object simultaneously, then compiler won’t thrown any runtime exception

This is the advantage of using ConcurrentHashMap over HashMap

In the demo example,

  • 1st thread iterates through all key-value pairs of ConcurrentHashMap
  • While other thread can safely remove key-value pair with key=1
  • Compiler doesn’t throws any ConcurrentModificationException unlike HashMap
  • This is because, ConcurrentHashMap works on different concurrency level or different locking strategy

IterateAndModifyConcurrentHashMapSimultaneously.java

package in.bench.resources.concurrent.collection;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

// implementing Runnable interface
public class IterateAndModifyConcurrentHashMap implements Runnable {

	// creating ConcurrentHashMap object of type <Integer, String>
	static ConcurrentHashMap<Integer, String> chm =
			new ConcurrentHashMap<Integer, String>();

	@Override
	public void run() {

		try {
			// sleeping thread for 1000 ms
			Thread.sleep(1000);

			// removing entry with key=1
			String value = chm.remove(1);
			System.out.println("Entry with {key=1"
					+ " and value=" + value + "} is removed");
		}
		catch(InterruptedException iex) {
			iex.printStackTrace();
		}
		System.out.println("Removal is done... !!");
	}

	/**
	 * main() method
	 * @param args
	 * @throws InterruptedException
	 */
	public static void main(String[] args) throws InterruptedException {

		// adding key-value pairs to ConcurrentHashMap object
		chm.put(1, "google.com");
		chm.put(2, "youtube.com");
		chm.put(3, "facebook.com");

		// creating another thread
		Thread newThread = new Thread(
				new IterateAndModifyConcurrentHashMap());
		newThread.start();

		// iterating CHM object using enhanced for-loop
		for(Map.Entry<Integer, String> me : chm.entrySet()) {

			System.out.println("{Key=" + me.getKey()
					+ "\t" + "Value=" + me.getValue() + "}");

			// sleeping thread for 2000 ms, after every turn
			Thread.sleep(2000);
		}
		System.out.println("Iterating completed... !!");
	}
}

Output:

{Key=3	Value=facebook.com}
Entry with {key=1 and value=google.com} is removed
Removal is done... !!
{Key=2	Value=youtube.com}
Iterating completed... !!

Explanation:

  • When we executed same program replacing HashMap with ConcurrentHashMap, then program executed without any runtime exception like ConcurrentModificationException
  • But there might be different output at different execution point
  • Reason : Because, while one thread iterating through all entries, it might get updated entries got from 2nd thread
  • In the above example, we have got updated entries and it is possible because 1st thread which is iterating got updation from 2nd thread (removal)
  • Same isn’t true with next iteration, because next time there might be possibility of iterating all entries (in this case 1st doesn’t get updation from 2nd thread)

Lets us print other possibility as well

Output:

{Key=1	Value=google.com}
Entry with {key=1 &amp; value=google.com} is removed
Removal is done... !!
{Key=2	Value=youtube.com}
{Key=3	Value=facebook.com}
Iterating completed... !!

From above output, it is clear that 1st thread isn’t got updation from 2nd thread and 1st thread iterated through all entries

Related Articles:

References:

Happy Coding !!
Happy Learning !!

Java 5 - ConcurrentHashMap v/s HashMap
Java 5 - ConcurrentHashMap class with example