併發修改異常
併發修改異常:ConcurrentModificationException
1.產生原因:迭代器遍歷的過程中,通過集合對象修改了集合中的元素,造成了迭代器獲取元素中判斷預期修改值和實際修改值不一致
2.解決方案:用for循環遍歷,然後用集合對集合對象做對應的操作即可
3.在瞭解併發修改異常的時候,需要一段適合的代碼來進行演示出現併發修改異常.
演示代碼:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ConcurrenceTest {
public static void main(String[] args) {
//創建集合對象
List<String> list = new ArrayList<>();
//給集合添加元素
list.add("hello");
list.add("world");
list.add("java");
//創建迭代器對象
Iterator<String> it = list.iterator();
//給出判斷條件並添加元素
while (it.hasNext()) {
String s = it.next();
if (s.equals("world")) {
list.add("javaee");
}
}
System.out.println(list);
}
}
此程序在運行之後會報錯:ConcurrentModificationException,那麼很明顯這是一個運行時異常,我們點擊ArrayList跟進查看源碼,研究爲什麼會出現這個異常.
我們找到List查看源碼,這個List接口裏面有兩個方法:
public interface List<E> {
Iterator<E> iterator();
boolean add(E e);
}
下面是ArrayList的父類,所以兒子ArrayList是可以繼承到父類的modCount的
public class ArrayList<E> {
//給實際集合修改次數賦初值爲0
protected int modCount = 0;
}
我們new的是一個ArrayList集合的對象,所以我們得有ArrayList這個類,這個類實現了接口AbstractList,所以要重寫方法,下面的代碼將進行詳細解釋,從每一步的註釋當中將會知道併發修改異常的產生原因:
public class ArrayList<E> extends AbstractList<E> implements List<E> {
public boolean add(E e) {
//實際集合修改次數加1,而預期集合修改次數沒有做++操作
modCount++;
add(e, elementData, size);
return true;
}
//迭代器對象,調用了Itr()方法
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
//對預期集合修改次數進行賦值,這裏modCount=0,賦值給了expectedModCount=0
int expectedModCount = modCount;
/*
modCount:實際集合修改次數
expectedModCount:預期集合修改次數
*/
public E next() {
//調用方法進行判斷
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
//在next方法中調用了checkForComodification方法
final void checkForComodification() {
//在add方法中實際集合修改次數加1,modCount=1,而預期集合修改次數沒有做++操作,expectedModCount=0,所以兩個的值不相等
if (modCount != expectedModCount)
//倆值不相等時拋出異常
throw new ConcurrentModificationException();
}
}
4.既然使用這種方法進行添加元素會拋出異常,那麼我們有沒有方法能夠避免這種異常呢?肯定是有的.
使用普通for.
代碼實現:
import java.util.ArrayList;
import java.util.List;
public class ConcurrenceTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
if (s.equals("hello")) {
list.add("javaee");
}
}
System.out.println(list);
}
}
在此程序運行之後沒有問題,那麼在list.get(i)方法獲取集合元素的時候有沒有對實際集合修改次數做操作呢
我們點擊get跟進查看源碼:
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
可以看到在這個方法裏面並沒有對實際集合修改次數(modCount)做++操作,那麼也就不會調用checkForComodification()方法進行判斷,所以這裏不會拋出併發異常.