Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SynchronizedMap ConcurrentModificationException

I am trying to understand the SynchronizedMap and I ran the below code. I get the below Output with an exception. According to my understanding the exception is caused when the get() methods are trying to access the syncmap when a thread is still executing a write on the map or Thread is in the Sleep state. Is my understanding correct or am I missing something ?

class MapHelper1 implements Runnable {
Map<String, Integer> map;

public MapHelper1(Map<String, Integer> map) {
    this.map = map;
    new Thread(this, "MapHelper1").start();
}

public void run() {
    map.put("One", 1);
    try {
        System.out.println("MapHelper1 sleeping");
        Thread.sleep(100);
    } catch (Exception e) {
        System.out.println(e);
    }

}
}

class MapHelper2 implements Runnable { 
Map<String, Integer> map;

public MapHelper2(Map<String, Integer> map) {
    this.map = map;
    new Thread(this, "MapHelper3").start();
}

public void run() {
    map.put("two", 1);
    try {
        System.out.println("MapHelper2 sleeping");
        Thread.sleep(100);
    } catch (Exception e) {
        System.out.println(e);
    }

}
}


class MapHelper3 implements Runnable {
Map<String, Integer> map;

public MapHelper3(Map<String, Integer> map) {
    this.map = map;
    new Thread(this, "MapHelper3").start();
}

public void run() {
    map.put("three", 1);
    try {
        System.out.println("MapHelper3 sleeping");
        Thread.sleep(100);
    } catch (Exception e) {
        System.out.println(e);
    }

}
}

public class MainClass {

public static void main(String[] args) {
    Map<String, Integer> hashMap = new HashMap<>();
    Map<String, Integer> syncMap = Collections.synchronizedMap(hashMap);
    MapHelper1 mapHelper1 = new MapHelper1(syncMap);
    MapHelper2 mapHelper2 = new MapHelper2(syncMap);
    MapHelper3 mapHelper3 = new MapHelper3(syncMap);



    for (Map.Entry<String, Integer> e : syncMap.entrySet()) {
        System.out.println(e.getKey() + "=" + e.getValue());
    }

}

}

OUTPUT:

MapHelper1 sleeping
MapHelper2 sleeping
MapHelper3 sleeping


Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1494)
at java.base/java.util.HashMap$EntryIterator.next(HashMap.java:1527)
at java.base/java.util.HashMap$EntryIterator.next(HashMap.java:1525)
at MainClass.main(MainClass.java:137)
Command exited with non-zero status 1

EDIT : I ran the code again a few times when the output was generated without exception. Why is this behavior?

like image 364
ghostrider Avatar asked Jan 18 '18 14:01

ghostrider


People also ask

How do I fix ConcurrentModificationException in Java?

How do you fix Java's ConcurrentModificationException? There are two basic approaches: Do not make any changes to a collection while an Iterator loops through it. If you can't stop the underlying collection from being modified during iteration, create a clone of the target data structure and iterate through the clone.

Does synchronized HashMap throws ConcurrentModificationException?

A retrieval operation will return the value inserted by the most recent completed insert operation. A lock is required for read operation too in SynchronizedHashMap. ConcurrentHashMap doesn't throw a ConcurrentModificationException if one thread tries to modify it while another is iterating over it.

What is the difference between synchronizedMap and ConcurrentHashMap?

synchronizedMap() requires each thread to acquire a lock on the entire object for both read/write operations. By comparison, the ConcurrentHashMap allows threads to acquire locks on separate segments of the collection, and make modifications at the same time.

What causes ConcurrentModificationException?

What Causes ConcurrentModificationException. The ConcurrentModificationException generally occurs when working with Java Collections. The Collection classes in Java are very fail-fast and if they are attempted to be modified while a thread is iterating over it, a ConcurrentModificationException is thrown.

How ConcurrentModificationException can be handled?

Q) How ConcurrentModificationException can be handled ? Or else, go for concurrent collection introduced in Java 1.5 version like ConcurrentHashMap instead of HashMap which works on different locking strategies How ConcurrentModificationException is thrown while iterating Map & simultaneously removing an entry using enhanced forEach loop

What is the difference between ConcurrentHashMap and synchronizedMap?

The Differences Collections.synchronizedMap () and ConcurrentHashMap both provide thread-safe operations on collections of data. The Collections utility class provides polymorphic algorithms that operate on collections and return wrapped collections. Its synchronizedMap () method provides thread-safe functionality.

What is the use of synchronizedMap() method in Java?

The synchronizedMap () method is used to return a synchronized (thread-safe) map backed by the specified map. This method is present in java.util.Collections. public static <K,V> Map<K,V> synchronizedMap (Map<K,V> M) // where M is the map to be synchronized K is key // and V is value for the resultant synchronized map.

What is ConcurrentHashMap in JDK?

ConcurrentHashMap was introduced in JDK 1.5 as an enhancement of HashMap that supports high concurrency for retrievals as well as updates. HashMap isn't thread-safe, so it might lead to incorrect results during thread contention. The ConcurrentHashMap class is thread-safe.


2 Answers

You dont synchronize access when iterating. Use:

synchronized(syncMap) {
  for (Map.Entry<String, Integer> e : syncMap.entrySet()) {
    System.out.println(e.getKey() + "=" + e.getValue());
  }
}

It's even in the synchronizedMap() method javadoc

It is imperative that the user manually synchronize on the returned map when iterating over any of its collection views:

  Map m = Collections.synchronizedMap(new HashMap());
      ...
  Set s = m.keySet();  // Needn't be in synchronized block
      ...
  synchronized (m) {  // Synchronizing on m, not s!
      Iterator i = s.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }
like image 144
Dariusz Avatar answered Oct 22 '22 03:10

Dariusz


I just read @Dariusz answer, I think it's totally correct.

To answer specifically why you saw random behavior, it's completely timing. That is, if the map had been populated by all three threads before iteration in Main thread, then everything is fine. But if one thread try to populate while iteration is already underway in Main thread, then you get the exception.

Btw, I know this is for demo purpose. But in real code, better not to start the thread in constructor.

like image 34
glithedev Avatar answered Oct 22 '22 02:10

glithedev