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()方法進行判斷,所以這裏不會拋出併發異常.

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