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); } } }
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 }
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); } } }
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(); }
在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()
方法就会抛出异常,因为此时List
的modCount
已经变为 了2,而expectedModCount
仍然是3,所以会抛出ConcurrentModificationException
异常;
那么如何解决该问题呢?我们查看java.util.ArrayList.Itr
(ArrayList中的Iterator实现)的源码可以发现,在该迭代器中有一个remove
方法可以 删除当前迭代元素,而且会同时修改modCount
和expectedModCount
,这样在进行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(); } }
其中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(); } } }
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 checkForComodification
keine 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: 🎜rrreeeDas 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!