Found the problem

I recently in the debug program bug, met a strange bug, is to traverse a hashmap set, do the remove operation at the same time, raise the Java. This eventually led to util. ConcurrentModificationException error. With doubts, the following reference to the source code, analysis of the cause of the problem. First, reproduce the problem, construct a map and add elements to it:

private static HashMap<Integer, String> map = new HashMap<Integer, String>();;
	public static void main(String[] args) {
  	        for(int i = 0; i < 10; i++){  
	            map.put(i, "value"+ i); }}Copy the code

Then remove some of the elements, at this time will be reported to the Java. Util. ConcurrentModificationException error

    for(Map.Entry<Integer, String> entry : map.entrySet()){  
         Integer key = entry.getKey();  
         if(key % 2 == 0){  
             System.out.println("To delete key " + key);  
             map.remove(key);  
             System.out.println("The key " + + key + " was deleted");  
         }  
Copy the code

To analyze problems

HashMap$hashiterator.nextnode () {HashMap$hashiterator.nextnode (); = this. ExpectedModCount was established

Let’s look again at what the hashmap remove operation does:

ModCount is incremented here, which means action +1. What are modCount and expectedModCount

Question why

You can see that modCount and expectedModCount are synchronized when the iterator is initialized. Here, you can see the reason for the error:

  • The HashMap maintains a modCount variable and the iterator maintains a expectedModCount variable, which are the same at first.
  • Each time the hashmap.remove operation is performed, the modCount is expectedModCount is the same as before.
  • On the next() call to the iterator, determine whether hashmap.this.modcount! ExpectedModCount = this.expectedModCount, and if so, an exception is thrown.

To solve the problem

When can you delete elements from a map while iterating through it? Take a look at the remove method provided by the iterator:

You can see that when the iterator removes an element, the expectedModCount is reassigned so that it does not get an error when iterating again. So the previous code can be written as follows, calling the iterator’s remove method directly.

 Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
      while(it.hasNext()){
          Map.Entry<Integer, String> entry = it.next();
          Integer key = entry.getKey();
          if(key % 2 == 0){
         	 System.out.println("To delete key " + key);
         	 it.remove();    
         	 System.out.println("The key " + + key + " was deleted"); }}Copy the code

conclusion

  • Basically Java collection classes (including the list and map) traversed the useless iterator to delete the quote ConcurrentModificationException mistakes, this is a fast – fail mechanism, is designed to test the bug.
  • In general terms, this mechanism is designed to prevent high concurrency situations where multiple threads modify elements of a map or list at the same time, resulting in data inconsistencies. = expectedModCount tells you that another thread has modified the set.

Replacement mechanism:

  • Use the remove method of the iterator.
  • Use currentHashMap instead of HashMap