Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to recreate java.util.ConcurrentModificationException

Code looks like this: The maps being used here are Guava maps

private Map<SomeObject, SomeOtherObject> myMap = Maps.newLinkedHashMap();

public Map<SomeObject, SomeOtherObject> getMap() {
  return Maps.newHashMap(myMap);
}

public void putMap(SomeObject a, SomeOtherObject b) {
  myMap.put(a,b);
}

So, the above threw java.util.ConcurrentModificationException and am trying to recreate the scenario. But no matter what I try, the system seems resilient. Here is what I tried:

 1. Created 'n' threads some of which call getMap() and some call putMap()
 2. Created 'n' threads that only call putMap() and one thread the is in an infinite loop that calls getMap()
 3. Created 2 threads each of which alternates calling getMap() and putMap(). Meaning, thread-1 first calls get then put and thread-2 first calls put and then get.

None of the above works, either keeps running or goes to OOM. Any pointers on how to go about doing this?

EDIT I believe the ConcurrentModificationException is thrown when returning the copy of the map {Maps.newHashMap(myMap);}. During this process the iterator creates a copy and while the iterator is at work, if the contents of the map are modified its not happy.

like image 446
noMAD Avatar asked Aug 16 '13 20:08

noMAD


People also ask

How do I fix Java Util ConcurrentModificationException?

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.

What is Java Util ConcurrentModificationException?

The java. util. concurrentmodificationexception is an error in Java. The error occurs when the iterator is traversing a list, and a command is used to change an element's value during that traversal.


1 Answers

Assuming that you indeed use com.google.common.collect.Maps, the implementation of newHashMap is

public static <K, V> HashMap<K, V> newHashMap(Map<? extends K, ? extends V> map) {
    return new HashMap<K, V>(map);
}

If we then look at the implementation of HashMap:

public HashMap(Map<? extends K, ? extends V> m) {
    this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
            DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
    putAllForCreate(m);
}

and

private void  [More ...] putAllForCreate(Map<? extends K, ? extends V> m) {
    for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i 
            = m.entrySet().iterator(); i.hasNext(); ) {
        Map.Entry<? extends K, ? extends V> e = i.next();
        putForCreate(e.getKey(), e.getValue());
    }
}

So indeed, the call to newHashMap uses an iterator to traverse the map. As already pointed out by other answers, if putMap is called while iterating the map, this will throw a ConcurrentModificationException.

How can you reproduce this? I would say that two threads are enough: one repetitively calls getMap, the other one putMap.

like image 141
Vincent van der Weele Avatar answered Oct 05 '22 23:10

Vincent van der Weele