Java学习笔记之并发修改异常(源码解析)

并发修改异常

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章