Java 例外: Java.util.ConcurrentModificationException 例外処理

ヒント: 記事を作成した後、目次を自動的に生成できます。生成方法は、右側のヘルプドキュメントを参照してください。

Java 例外: Java.util.ConcurrentModificationException 例外処理


序文

開発では、List を走査するためにイテレータが使用され、java.util.ConcurrentModificationException エラーが発生しました。この記事では、このエラーの原因と解決方法を記録します。


1. イテレータ Iterator とは何ですか?

イテレータは設計パターンの 1 つであり、イテレータ パターンは、オブジェクトの内部表現を保持せずに、集合オブジェクト内の要素に順次アクセスする方法を提供します。

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

一般的な使用形式は次のとおりです。

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 + " ");
        }
    }
}

二、Java.util.ConcurrentModificationException原因

典型的なコード例:

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);
    }
}

問題が発生します: list.remove() の代わりに Iterator.remove() を呼び出す必要があります!
ここに画像の説明を挿入
実行結果に java.util.ConcurrentModificationException 例外メッセージがスローされます。これはコレクションの同時変更例外が発生しているためで、次にソースコードから例外の原因を解析していきます。

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

ArrayList コレクションの Iterator メソッドでは、Itr オブジェクトを返すことでイテレータを取得します。Itr は ArrayList の内部クラスであり、Iterator インターフェイスを実装します。コードは次のとおりです。

   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 クラス属性

属性 意味
カーソル インデックス添字。次にアクセス可能な要素のインデックスを示します。デフォルト値は 0 です。
最終更新 前の要素のインデックスを示すインデックス添字。デフォルト値は -1 です。
期待されるModCount コレクションに対して変更されたバージョン番号。初期値は ModCount です。

ModCount は AbstractList インターフェイスで定義され、初期値は 0 で、定義は次のとおりです。

 protected transient int modCount = 0;

ModCount はバージョン番号で、コレクションが変更されると (追加、削除、変更など)、バージョン番号は +1 されます。

上記のコードを結合して、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快速失败机制,快速判断是否存在同时操作问题。因此,集合在多线程下使用是不安全的。

特記事項!
この記事は主に以下を参照しています: Java イテレータの詳細な説明、この記事を読むだけで十分です

おすすめ

転載: blog.csdn.net/qq_46119575/article/details/131580050