Java exception: Java.util.ConcurrentModificationException exception handling

Tip: After the article is written, the table of contents can be automatically generated. How to generate it can refer to the help document on the right

Java exception: Java.util.ConcurrentModificationException exception handling


foreword

In the development, an iterator is used to traverse the List, and a java.util.ConcurrentModificationException error is encountered. This article records the cause of this error and how to solve it


1. What is iterator Iterator?

The iterator is one of the design patterns, and the iterator pattern provides a way to sequentially access the elements in an aggregate object without retaining the internal representation of the object.

1)Iterator对象称为迭代器,主要用于遍历Collection集合中的元素。
2)所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。
3)Iterator仅用于遍历集合,Iterator本身并不存放对象。

Common usage forms are:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        List<Integer> list = new ArrayList<>();
        list.add(111);
        list.add(222);
        list.add(333);

        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()) {
    
    
            int value = iterator.next();
            System.out.print(value + " ");
        }
    }
}

Two, Java.util.ConcurrentModificationException cause

A typical code example:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@SuppressWarnings({
    
    "all"})
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        List<Integer> list = new ArrayList<>();
        list.add(111);
        list.add(222);
        list.add(333);

        System.out.println("删除前:" + list);
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()) {
    
    
            Integer value =  iterator.next();
            if(value == 111) list.remove(Integer.valueOf(111));
        }
        System.out.println("删除后" + list);
    }
}

The problem arises: Iterator.remove() should be called instead of list.remove()!
insert image description here
A java.util.ConcurrentModificationException exception message is thrown in the running result. This is because the exception of concurrent modification in the collection is triggered. Next, we analyze the cause of the exception through the source code.

    public Iterator<E> iterator() {
    
    
        return new Itr();
    }

In the Iterator method of the ArrayList collection, the iterator is obtained by returning the Itr object. Itr is an internal class of ArrayList, which implements the Iterator interface, the code is as follows:

   private class Itr implements Iterator<E> {
    
    
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {
    
    }

        public boolean hasNext() {
    
    
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
    
    
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
    
    
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
    
    
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
    
    
                throw new ConcurrentModificationException();
            }
        }

Itr class attribute

Attributes meaning
cursor Index subscript, indicating the index of the next accessible element, the default value is 0
lastRet Index subscript, indicating the index of the previous element, the default value is -1
expectedModCount The version number modified for the collection, the initial value is ModCount

ModCount is defined in the AbstractList interface, the initial value is 0, and the definition is as follows:

 protected transient int modCount = 0;

ModCount is the version number, and the version number will be +1 when the collection is changed (add, delete, modify, etc.).

Combine the above code to explain the exception of java.util.ConcurrentModificationException.

①初始化ArrayList,添加三次元素,即三次调用add()方法,进行三次modCount++; 此时,m o d C o u n t = 3 , s i z e = 3 ; \color{
    
    red}{
    
    modCount = 3,size = 3}modCount=3,size=3
②初始化Iterator迭代器进行循环,此时,e x p e c t e d M o d C o u n t = m o d C o u n t = 3 , \color{
    
    red}{
    
    expectedModCount = modCount=3}expectedModCount=modCount=3, c u r s o r = 0 , l a s t R e t =1 \color{
    
    red}{
    
    cursor=0,lastRet = -1}cursor=0,lastRet=1
③进行hasNext判断,cursor != size;成立,进入循环
④调用next()方法,首先进行checkForComodification()校验,m o d C o u n t = = e x p e c t e d M o d C o u n t \color{
    
    red}{
    
    modCount == expectedModCount}modCount==expectedModCount,校验通过,返回值,此时l a s t R e t = 0 ; c u r s o r = 1 \color{
    
    red}{
    
    lastRet = 0;cursor = 1}lastRet=0;cursor=1

 final void checkForComodification() {
    
    
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
  }
  
⑤调用集合remove()方法,modCount++;,此时}modCount=4;size=2
⑥再次调用hasNext()方法判断,cursor != size;成立,进入循环
⑦调用next()方法进行校验,modCount ! = expectedModCount,校验未通过,抛出java.util.ConcurrentModificationException异常

总结:

①在使用迭代器的remove()操作时,会将更新后的modCount给expectedModCount,两者会得到同步,但是在调用集合的remove()方法后,两个不会进行同步,进而导致在checkForComodification()校验时不通过,抛出java.util.ConcurrentModificationException异常。
②所以,在单线程下使用迭代器是没有问题的,但是在多线程下同时操作集合就不允许了,可以通过fail-fast快速失败机制,快速判断是否存在同时操作问题。因此,集合在多线程下使用是不安全的。

Special Note!
This article mainly refers to : Detailed explanation of Java iterators, it is enough to read this article

Guess you like

Origin blog.csdn.net/qq_46119575/article/details/131580050