ホームページ  >  記事  >  Java  >  JavaでConcurrentModificationException例外警告を解決する方法

JavaでConcurrentModificationException例外警告を解決する方法

王林
王林転載
2023-05-15 13:01:06807ブラウズ

例外分析

Java コードを書いたことがある人なら誰でも、この例外に遭遇したことがあると思います。通常、この例外は次のコードによって引き起こされます。

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);
      }
    }
}
上記のコードは最終的に java.util.ConcurrentModificationException をスローしますが、なぜでしょうか?まず、上記のコードを逆コンパイルすると、次の結果が得られます (foreach 構文シュガーをよく知っている場合は無視しても構いません):

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
}
上記のコードの翻訳は、次のコードと同等です:

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);
      }
    }
}
次に、

iterator.hasNext() ソース コードを見ると、最初の行が checkForComodification メソッドを呼び出していることがわかります。このメソッドを確認してみましょう:

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}
in

modCount != ExpectedModCount この条件が成立すると、ConcurrentModificationException 例外がスローされます。では、この条件はどのように成立するのでしょうか?

1. まず、

modCount のソースを確認すると、modCount の値が、現在のリスト。List を呼び出すとき。.remove メソッドを使用すると、modCount もそれに応じて 1 減らされます。2。次に、## のソースを確認します。 #expectedModCount が構築されていることがわかります。 ##Iterator

(ここでは ArrayList の内部実装が使用されています) の場合、変数の割り当てと

modCount の値が存在します。は expectedModCount; 3. 最後に、ループを実行して List.remove メソッドを呼び出すと、modCount

は変更されますが、

expectedModCount は変更されません。最初のループが終了すると、削除されます。データがループ内で 2 回目に iterator.hasNext() メソッドを呼び出す準備ができたとき、checkForComodification() メソッドは例外をスローします。これは、この時点で List modCount が 2 に変更されており、expectedModCount がまだ 3 であるためです。 ConcurrentModificationException 例外がスローされます;

Solution

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

以上がJavaでConcurrentModificationException例外警告を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。