Heim > Java > javaLernprogramm > So lösen Sie die ConcurrentModificationException-Ausnahmewarnung in Java

So lösen Sie die ConcurrentModificationException-Ausnahmewarnung in Java

王林
Freigeben: 2023-05-15 13:01:06
nach vorne
898 Leute haben es durchsucht

Ausnahmeanalyse

Ich glaube, dass jeder, der Java-Code geschrieben hat, auf diese Ausnahme gestoßen ist, die normalerweise durch den folgenden Code verursacht wird:

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

public class Test{
    public static void main(String[] args){
      List<String> list = new ArrayList<>();
      list.add("123");
      list.add("456");
      list.add("789");
      for(String obj : list){
          list.remove(obj);
      }
    }
}
Nach dem Login kopieren

Der obige Code wird schließlich java.util.ConcurrentModificationException verursachen, also warum ? Zuerst dekompilieren wir den obigen Code und erhalten die folgenden Ergebnisse (Sie können es ignorieren, wenn Sie die foreach-Syntax Sugar besser kennen):

public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 4: 0

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #4                  // String 123
      11: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      16: pop
      17: aload_1
      18: ldc           #6                  // String 456
      20: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      25: pop
      26: aload_1
      27: ldc           #7                  // String 789
      29: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      34: pop
      35: aload_1
      36: invokeinterface #8,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
      41: astore_2
      42: aload_2
      43: invokeinterface #9,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
      48: ifeq          72
      51: aload_2
      52: invokeinterface #10,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      57: checkcast     #11                 // class java/lang/String
      60: astore_3
      61: aload_1
      62: aload_3
      63: invokeinterface #12,  2           // InterfaceMethod java/util/List.remove:(Ljava/lang/Object;)Z
      68: pop
      69: goto          42
      72: return
    LineNumberTable:
      line 6: 0
      line 7: 8
      line 8: 17
      line 9: 26
      line 10: 35
      line 11: 61
      line 12: 69
      line 13: 72
}
Nach dem Login kopieren

Die Übersetzung des obigen Codes entspricht dem folgenden Code:

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

public class Test{
    public static void main(String[] args){
      List<String> list = new ArrayList<>();
      list.add("123");
      list.add("456");
      list.add("789");
      Iterator<String> iterator = list.iterator();
      while (iterator.hasNext()){
          String obj = iterator.next();
          list.remove(obj);
      }
    }
}
Nach dem Login kopieren

Dann schauen wir uns iterator.hasNext( ) können Sie feststellen, dass in der ersten Zeile die Methode checkForComodification aufgerufen wird: iterator.hasNext()源码,可以发现第一行调用了checkForComodification方法,我们查看这个方法:

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}
Nach dem Login kopieren

modCount != expectedModCount这个条件成立的时候会抛出ConcurrentModificationException异常,那么这个条件是怎么成立的呢?

1、首先我们查看modCount的来源,可以发现modCount的值等于当前List的size,当调用List.remove方法的时候modCount也会相应的减1;

2、然后我们查看expectedModCount的来源,可以看到是在构造Iterator(这里使用的是ArrayList的内部实现)的时候,有一个变量赋值,将modCount 的值赋给了expectedModCount

3、最后当我们执行循环调用List.remove方法的时候,modCount改变了但是expectedModCount并没有改变,当第一次循环结束删除一个数据准 备第二次循环调用iterator.hasNext()方法的时候,checkForComodification()方法就会抛出异常,因为此时ListmodCount已经变为 了2,而expectedModCount仍然是3,所以会抛出ConcurrentModificationException异常;

解决方法

那么如何解决该问题呢?我们查看java.util.ArrayList.Itr(ArrayList中的Iterator实现)的源码可以发现,在该迭代器中有一个remove方法可以 删除当前迭代元素,而且会同时修改modCountexpectedModCount,这样在进行checkForComodification检查的时候就不会抛出异常了,该remove 方法源码如下:

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();
    }
}
Nach dem Login kopieren

其中ArrayList.this.remove(lastRet);这一行会改变modCount的值,而后边会同步的修改expectedModCount的值等于modCount

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

public class Test{
    public static void main(String[] args){
      List<String> list = new ArrayList<>();
      list.add("123");
      list.add("456");
      list.add("789");
      Iterator<String> iterator = list.iterator();
      while (iterator.hasNext()) {
          System.out.println("移除:" + iterator.next());
          iterator.remove();
      }
    }
}
Nach dem Login kopieren
Wenn die Bedingung modCount != erwartetModCount wahr ist, wird eine ConcurrentModificationException-Ausnahme ausgelöst. Wie wird diese Bedingung dann hergestellt?

1. Zuerst überprüfen wir die Quelle von modCount und stellen fest, dass der Wert von modCount gleich der size des Stroms ist Beim Aufruf von List wird modCount entsprechend um 1 reduziert /code> und wir können sehen, dass es einen Iterator erstellt (hier wird die interne Implementierung von ArrayList verwendet), es gibt eine Variablenzuweisung und der Wert von modCount ist zugewiesen an expectedModCount; 🎜🎜3 Wenn wir schließlich die Schleife ausführen und die Methode List.remove aufrufen, ändert sich modCount, aber Der erwartete ModCount ändert sich nicht, wenn die erste Schleife endet. Wenn Daten zum zweiten Mal in einer Schleife aufgerufen werden können, wird die checkForComodification() löst eine Ausnahme aus, da sich zu diesem Zeitpunkt die Liste Der modCount von code> auf 2 geändert hat und der Der erwartete ModCount ist immer noch 3, daher wird die Ausnahme ConcurrentModificationException ausgelöst. Wenn wir uns den Quellcode von java.util.ArrayList.Itr (Iterator-Implementierung in ArrayList) ansehen, können wir feststellen, dass es in diesem Iterator eine remove-Methode gibt, die dies kann Löschen Sie das aktuelle Iterationselement. Darüber hinaus werden modCount und expectedModCount gleichzeitig geändert, sodass bei der Ausführung von checkForComodificationkeine Ausnahme ausgelöst wird > prüfen. Der remove Der Quellcode der Methode lautet wie folgt: 🎜rrreee🎜 Unter anderem ändert <code>ArrayList.this.remove(lastRet); den Wert von modCount und wird später synchron geändert expectedModCount ist gleich dem Wert von modCount 🎜🎜Ändern Sie nun das Programm, mit dem wir begonnen haben folgt und es läuft normal: 🎜rrreee

Das obige ist der detaillierte Inhalt vonSo lösen Sie die ConcurrentModificationException-Ausnahmewarnung in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:yisu.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage