ArrayList什麼狀況會拋出ConcurrentModificationException

近日,在看ArrayList的源碼實現。發現不少狀況會拋出ConcurrentModificationException。下面總結一下大體發生的狀況。 首先,ArrayList不是線程安全的。 首先來看一個例子:java

public  static  void main(String[] args){
        List<Integer> aList = new ArrayList<Integer>();
        aList.add(1);
        aList.add(2);

        Iterator<Integer> iter = aList.iterator();
        aList.add(3);
        System.out.println(iter.next());
    }

運行結果:數組

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
	at java.util.ArrayList$Itr.next(ArrayList.java:831)
	at com.zhu.util.ArrayIistIteratorTest.main(ArrayIistIteratorTest.java:19)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

由例子可見,在調用iterator方法後,若是其餘線程對ArrayList進行了更改大小的操做,如add和remove。那麼將會拋出ConcurrentModificationException。字面意思很簡單:併發修改異常。安全

咱們經過源碼,來看看爲何會出現這樣的狀況: 首先ArrayList的iterator方法實現:併發

public Iterator<E> iterator() {
        return new Itr();
    }

Itr是ArrayList的內部類,實現了Iterator<E>接口,下面是Itr的源碼:app

private class Itr implements Iterator<E> {
        /**
         * Index of element to be returned by subsequent call to next.
         */
        int cursor = 0;

        /**
         * Index of element returned by most recent call to next or
         * previous.  Reset to -1 if this element is deleted by a call
         * to remove.
         */
        int lastRet = -1;

        /**
         * The modCount value that the iterator believes that the backing
         * List should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size();
        }

        public E next() {
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                AbstractList.this.remove(lastRet);
                if (lastRet < cursor)
                    cursor--;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

Itr只有三個成員變量:cursor,lastRet,exceptdModCountthis

  • cursor:next方法應該返回元素所在的下標
  • lastRet:上一次next返回元素的下標
  • exceptedModCount:ArrayList成員變量modCount的副本 ArrayList中modCount的意義是記錄當前ArrayList被修改的次數,ArrayList中會引發modCount發生改變的方法有如下集中:
    1. trimToSize:將數組大小減少至當前元素個數
    2. ensureCapacity:容量設置
    3. add:新增元素的時候
    4. remove:移除元素的時候
    5. clear:清空的時候 全部移除和新增的操做都會引發modCount的修改

能夠看出引發例子拋出異常的緣由是由於Itr建立以後,exceptedModCount爲當時ArrayList對象modCount的值。在Itr的next和remove方法中能夠看出,在世紀操做以前都會調用checkForComodification來檢查ArrayList是否被修改過。在調用Itr中next和remove與Itr建立之間,若是有其餘線程或本線程調用了引發ArrayList的modCount發生變化的操做,那麼將會拋出併發修改異常。線程

那麼下面咱們再來看看還有其餘什麼狀況,ArrayList會拋出CurrentModificationException。code

  1. writeObject:writeObject是序列化時調用的方法,也就是說在在元素序列化時,若是有其餘操做引發了modCount發生改變時會拋出併發修改異常。
  2. Itr的next和remove:在使用迭代器期間,其餘操做引發modCount改變時。
  3. ListItr的previous,next,set,remove,add:同Itr.
  4. SubList中的全部操做
相關文章
相關標籤/搜索