Java原始碼研究001:關於ArraytList/ List的併發修改異常
阿新 • • 發佈:2020-12-28
這個就是實現一個簡單的 ArrayList 的遍歷,如果存在一個為“aaa”的值,就新增一個“ccc”
package Array; import java.util.ArrayList; import java.util.Iterator;
public class demo2 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("aaa"); list.add("bbb"); demo2.bianli(list); } public static void bianli(ArrayList<String> list) { Iterator<String> it = list.iterator(); // 1.迭代器:java.util.ConcurrentModificationException // while(it.hasNext()) { // String s = it.next(); // if(s.equals("aaa")) { // list.add("ccc"); // } // } // 2.加強for迴圈:java.util.ConcurrentModificationException // for(String s : list) { // if(s.equals("aaa")) { // list.add("ccc"); // } // } // 3.普通for迴圈可以,因為用的是:list.get(i),用 it.next()就出錯 // 因為iterator原始碼裡要比對一個東西 for (int i = 0; i < list.size(); i++) { String s = list.get(i); if (s.equals("aaa")) { list.add("ccc"); } } System.out.println(list); } }
從上面的程式碼中可以發現,使用list.get(i)就可以完成需求,而使用it.next();就不行
我們看一下it.next()出了甚麼問題
首先根據
Iterator<String> it = list.iterator();
我們追蹤到list中的iterator的方法
package java.util; public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { public Iterator<E> iterator() { return new 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; // prevent creating a synthetic constructor Itr() {} 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 public void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); final int size = ArrayList.this.size; int i = cursor; if (i < size) { final Object[] es = elementData; if (i >= es.length) throw new ConcurrentModificationException(); for (; i < size && modCount == expectedModCount; i++) action.accept(elementAt(es, i)); // update once at end to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
此時發現了一個CheckForComodification()函式,單獨拿出來看一下
CheckForComodification()中我們看到丟擲了ConcurrentModificationException的錯誤的可能性是 條件 modCount != expectedModCount 為真
package java.util; public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
好的我們繼續debug,發現都是正常的,但是當if判斷中條件為真,要執行
list.add("ccc");
的時候,我們追蹤到這個list中的add方法
package java.util; public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { public boolean add(E e) { modCount++; add(e, elementData, size); return true; } }
我們可以看到modCount++,modCount的值被改變了,這就是modCount != expectedModCount 為真的情況
expected的意思是期望,也就是說期望值和實際值不同導致錯誤。
那麼怎麼解決,最簡單就是不使用it.next()
而使用list.get()!
&n