>Java >java지도 시간 >Java에서 ConcurrentModificationException 예외 경고를 해결하는 방법

Java에서 ConcurrentModificationException 예외 경고를 해결하는 방법

王林
王林앞으로
2023-05-15 13:01:06903검색

예외 분석

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 메서드를 호출하는 것을 확인할 수 있습니다. 이 메서드를 확인해 보겠습니다. 가 true이면 ConcurrentModificationException 예외가 발생합니다. 그러면 이 조건은 어떻게 설정됩니까? 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()方法就会抛出异常,因为此时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();
    }
}

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

1 먼저 modCount의 소스를 확인하면 modCount의 값이 현재의 size와 같은 것을 확인할 수 있습니다. List.List.remove 메소드를 사용하면 그에 따라 modCount도 1씩 감소합니다. 그런 다음 expectedModCount 그리고 <code> Iterator(여기서 ArrayList의 내부 구현이 사용됨)를 생성하는 것을 볼 수 있습니다. 변수 할당이 있고 modCount의 값은 다음과 같습니다. expectedModCount에 할당됨

3. 마지막으로 루프를 실행하고 List.remove 메서드를 호출하면 modCount는 변경되지만 ExpectModCount는 변경되지 않습니다. 첫 번째 루프가 끝나면 삭제됩니다. 데이터가 루프에서 두 번째로 iterator.hasNext() 메서드를 호출할 준비가 되면 checkForComodification() 메소드는 예외를 발생시킵니다. 왜냐하면 이때 List code>의 modCount가 2로 변경되고 ExpectModCount는 여전히 3이므로 ConcurrentModificationException 예외가 발생합니다. 🎜🎜🎜🎜🎜🎜Solution Method🎜🎜이 문제를 해결하는 방법은 무엇입니까? java.util.ArrayList.Itr(ArrayList의 Iterator 구현)의 소스 코드를 살펴보면 이 반복자에 다음을 수행할 수 있는 remove 메소드가 있음을 알 수 있습니다. 또한, modCountexpectedModCount가 동시에 수정되므로 checkForComodification를 수행할 때 예외가 발생하지 않습니다. > remove 메소드의 소스코드는 다음과 같습니다. 🎜<pre class="brush:php;toolbar:false">import java.util.List; import java.util.ArrayList; import java.util.Iterator; public class Test{    public static void main(String[] args){      List&lt;String&gt; list = new ArrayList&lt;&gt;();      list.add(&quot;123&quot;);      list.add(&quot;456&quot;);      list.add(&quot;789&quot;);      Iterator&lt;String&gt; iterator = list.iterator();      while (iterator.hasNext()) {          System.out.println(&quot;移除:&quot; + iterator.next());          iterator.remove();      }    } }</pre>🎜 그 중 <code>ArrayList.this.remove(lastRet);는 modCount이며 나중에 동기적으로 수정됩니다. expectedModCount의 값은 modCount의 값과 같습니다. 🎜🎜이제 우리가 시작한 프로그램을 다음과 같이 수정합니다. 다음과 같이 정상적으로 실행됩니다: 🎜rrreee

위 내용은 Java에서 ConcurrentModificationException 예외 경고를 해결하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제