首頁 > Java > java教程 > java中ConcurrentModificationException異常警告怎麼解決

java中ConcurrentModificationException異常警告怎麼解決

王林
發布: 2023-05-15 13:01:06
轉載
898 人瀏覽過

異常分析

相信寫過一些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();
}
登入後複製

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的值;

現在修改我們開頭的程式如下就可以正常運作了:

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中文網其他相關文章!

相關標籤:
來源:yisu.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板