多執行緒下使用List中的subList和remove方法產生的 java.util.ConcurrentModificationException 異常

牧码人_hh發表於2024-05-17

在說多執行緒操作List之前,我們先看下單執行緒下產生的問題:

單執行緒

List<Integer> listA=new ArrayList<>();
listA.add(1);
listA.add(2);
listA.add(3);
listA.add(4);
listA.add(5);
listA.add(6);


for(Integer a:listA){
  if (a==3) {
    listA.remove(3);
  }
}

//再次使用集合就會報錯

System.out.println("list元素:" + listA);

該段程式碼最終會丟擲 java.util.ConcurrentModificationException 錯誤,主要的原因是:

在List迴圈時,會初始化modCount=0;當該集合進行增加、刪除操作值,modCount會自增;而我們使用加強for迴圈時候,會把modCount初始化的值給迭代器中的expectedModCount,也就是說開始expectedModCount也是為0;但是當集合操作了新增元素,modCount++ ,但是expectedModCount沒有新增。導致modCount和expectedModCount不一致,當我們再次訪問這個原集合時候,就會丟擲java.util.ConcurrentModificationException 錯誤。

多執行緒

說明:subList 返回的是 ArrayList 的內部類 SubList,並不是ArrayList ,而是 ArrayList 的一個檢視,對於SubList子列表的所有操作最終會反映到原列表上,對SubList操作,其實也是對ArrayList 操作,比如刪除SubList中的元素,同時也會刪除ArrayList 的元素。 在subList場景中,高度注意對父集合元素的增加或刪除,均會導致子列表的遍歷、增加、刪除產生 ConcurrentModificationException 異常。

List<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        arrayList.add(4);
        arrayList.add(5);
        arrayList.add(6);


        Thread t1= new Thread(new Runnable() {
            @Override
            public void run() {
                List<Integer> list = arrayList.subList(0, 3);
                for (Integer integer : list) {
                    System.out.println(" "+integer);
                    if(integer == 2){
                        list.remove(integer);
                    }
                }
                System.out.println("進行元素的刪除操作");
                //arrayList.remove(4);
                System.out.println("list元素:" + list);
            }
        });
        Thread t2= new Thread(new Runnable() {
            @Override
            public void run() {
                List<Integer> list2 = arrayList.subList(4, 6);
                for (Integer integer : list2) {
                    System.out.println(" "+integer);
                    if(integer == 4){
                        list2.remove(integer);
                    }
                }
                System.out.println("進行元素的刪除操作");
                //arrayList.remove(4);
                System.out.println("list元素:" + list2);
            }
        });
        t1.start();
        t2.start();

ArrayList中有個protected transient int modCount = 0; 用來記錄當前ArrayList被修改的次數。

比如add(),remove()等都會導致modeCount增加: ArrayList.subList()會生成一個SubList的物件,SubList中有個對應modCount同步ArrayList中的modeCount: SubList物件每次再遍歷時,會將自己的modeCount與ArrayList的modeCount進行對比,如果兩個值不一樣就會報異常:ConcurrentModificationException

相關文章