fail-fast機制即為快速失敗機制,個人認為是一種防護措施,在集合結構發生改變的時候,使盡全力拋出ConcurrentModificationException,所以該機制大部分用途都是用來檢測Bug的; 下麵的代碼可以引發fail-fast fail-fast原理 每個集合都會實現可遍歷的介面 ...
fail-fast機制即為快速失敗機制,個人認為是一種防護措施,在集合結構發生改變的時候,使盡全力拋出ConcurrentModificationException,所以該機制大部分用途都是用來檢測Bug的;
下麵的代碼可以引發fail-fast
1 public static void main(String[] args) { 2 List<String> list = new ArrayList<>(); 3 for (int i = 0 ; i < 10 ; i++ ) { 4 list.add(i + ""); 5 } 6 Iterator<String> iterator = list.iterator(); 7 int i = 0 ; 8 while(iterator.hasNext()) { 9 if (i == 3) { 10 list.remove(3); 11 //list.add("11"); 添加元素同樣會引發 12 } 13 System.out.println(iterator.next()); 14 i ++; 15 } 16 }
fail-fast原理
每個集合都會實現可遍歷的介面,以上述代碼為例,集合調用iterator();方法的時候,其實是返回了一個new Itr();
/** * Returns an iterator over the elements in this list in proper sequence. * * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>. * * @return an iterator over the elements in this list in proper sequence */ public Iterator<E> iterator() { return new Itr(); }
以下是Itr源碼
/** * An optimized version of AbstractList.Itr */ private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") 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]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
Itr有3個重要屬性;
cursor是指集合遍歷過程中的即將遍歷的元素的索引
lastRet是cursor -1,預設為-1,即不存在上一個時,為-1,它主要用於記錄剛剛遍歷過的元素的索引。
expectedModCount它初始值就為ArrayList中的modCount(modCount是抽象類AbstractList中的變數,預設為0,而ArrayList 繼承了AbstractList ,所以也有這個變數,modCount用於記錄集合操作過程中作的修改次數)
由源碼可以看出,該異常就是在調用next()的時候引發的,而調用next()方法的時候會先調用checkForComodification(),該方法判斷expectedModCount與modCount是否相等,如果不等則拋異常了
那麼問題就來了,初始化的時候expectedModCount就被賦值為modCount,而且源碼當中就一直沒有改變過,所以肯定是modCount的值變了
arrayList繼承了abstractList,abstractList有modCount屬性,通過以下源碼我們可以看到,當ArrayList調用add、remove方法,modCount++
/** * Inserts the specified element at the specified position in this * list. Shifts the element currently at that position (if any) and * any subsequent elements to the right (adds one to their indices). * * @param index index at which the specified element is to be inserted * @param element element to be inserted * @throws IndexOutOfBoundsException {@inheritDoc} */ public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } /** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). * * @param index the index of the element to be removed * @return the element that was removed from the list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }
所以由此可見,對集合的操作中若modCount發生了改變,則會引發fail-fast機制;同時可以看出如果想要移除集合某元素,可以使用迭代器的remove方法,則不會引發fail-fast;
發表該文章也參考了許多另一片文章的內容,詳情地址:https://blog.csdn.net/zymx14/article/details/78394464