并发修改异常
并发修改异常: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()方法进行判断,所以这里不会抛出并发异常.