1.ConcurrentModificationException错误
1.1 错误类型
由单词直译可知,这是并发修改错误,它是一个RuntimeException,jdk1.8中说到:
当不允许这样的修改时,可以通过检测到对象的并发修改的方法来抛出此异常。
1.2 发生原因
ArrayList是一个不安全的线程,当多个线程对同一个集合的内容进行操作时,就可能会抛出 ConcurrentModificationException 异常,产生fail-fast事件(fail-fast 机制是java集合(Collection)中的一种错误机制)。
注意:
如果单线程违反了规则,同样也有可能会抛出该异常。
迭代器遍历的过程中,通过集合对象修改了集合中的元素,造成了迭代器获取元素中判断预期修改值和实际修改值不一致,ConcurrentModificationException,
这是一段单线程会抛 ConcurrentModificationException 异常的错误。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object value = iterator.next();
System.out.println(value);
list.add("d");
}
}
解决方案
用for循环遍历,然后用集合对象做对应的操作即可
2. 查看源码,分析异常
此代码由copy而来,方便分析异常
首先来了解两个属性
属性: modCount:实际修改集合的次数 expectedModCount:预期修改集合的次数
2.1 查看源码
此代码由copy而来,方便分析异常
public interface List<E> {
Iterator<E> iterator();
boolean add(E e);
}
//抽象类
public abstract class AbstractList<E> {
protected int modCount = 0;
}
//ArrayList实现了抽象类,自然也拥有了modCount属性
public class ArrayList<E> extends AbstractList<E> implements List<E> {
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
//add 和 remove 都进行修改了modCount的值,自行查看remove方法
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
//使用迭代器进行遍历时
public Iterator<E> iterator() {
return new Itr();
}
//迭代器的实现类
private class Itr implements Iterator<E> {
//modCount进行赋值了
int expectedModCount = modCount;
//使用next进行取值时,
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];
}
//抛出异常
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
2.2 对异常进行源码分析:
发生原因:
- ArrayList 继承 AbstractList 抽象类
- AbstractList 里面有个 modCount 属性
- 在使用迭代器(Iterator)进行遍历的时候
首先把 modCount 进行备份,即(int expectedModCount = modCount;)
其次在使用 next() 方法首先是判断 modCount 是否等于 expectedModCount
相等时会继续执行代码
不等时则抛出 ConcurrentModificationException ,即并发异常
3. 总结
ArrayList、LinkList等不安全集合会造成ConcurrentModificationException,一方面是单线程的不正确使用,另一方面是多个线程对同一集合的操作,以后遇到这种问题可以查阅源码浏览 modCount 和 expectedModCount 进行查明原因。