在說多執行緒操作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