Java集合-05fail-fast(快速失敗)機制原理及解決方法
阿新 • • 發佈:2018-12-18
fail-fast簡介
fail-fast(快速失敗),是Java集合的一種錯誤檢測機制。當在遍歷集合的過程中該集合在結構(改變集合大小)上發生變化時候,
有可能發生fail-fast,丟擲java.util.ConcurrentModificationException異常。
fail-fast出現場景
- 單執行緒場景
public class FailFastSingleThreadTest { public static void main(String[] args) { List<String> lists = new ArrayList<>(10); for (int i = 0; i < 4; i++){ lists.add(String.valueOf(i)); } //fail-fast for (String list : lists) { lists.remove(3); } } } //output:Exception in thread "main" java.util.ConcurrentModificationException
- 多執行緒場景
public class FailFastMultiThread { private static List<String> lists = new ArrayList<>(10); static { for (int i = 0; i < 4; i++){ lists.add(String.valueOf(i)); } } public static void main(String[] args) { new Thread(new ForEachThread()).start(); new Thread(new EditThread()).start(); } //用於遍歷 static class ForEachThread implements Runnable{ @Override public void run() { Iterator<String> iterator = lists.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); try { Thread.sleep(100);//為了另外的執行緒加入,也是為了結合在遍歷時候修改結構 } catch (InterruptedException e) { e.printStackTrace(); } } } } //用於修改結構 static class EditThread implements Runnable{ @Override public void run() { lists.add("8"); } } } //output:Exception in thread "Thread-0" java.util.ConcurrentModificationException
產生原因
集合能夠遍歷是因為迭代器的原因,而Iterator介面只是定義了具體的方法,集合需要實現該介面方法,
檢視ArrayList中具體的實現方法//省略部分方法 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; @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(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
可以看出在Itr是ArrayList的一個內部類,迭代器操作通過這個內部類,Itr有個expectedModCount屬性,這個屬性判斷是否與modCount相等,如果不相等丟擲異常,modCount記錄list結構上發生變化的次數,可以看出在迭代時候checkForComodification()方法檢測兩個的值不相等就丟擲異常
解決方法
- 單執行緒
- 使用迭代器的remove方法
public class NoFailFastSingleThread { public static void main(String[] args) { List<String> lists = new ArrayList<>(10); for (int i = 0; i < 4; i++){ lists.add(String.valueOf(i)); } Iterator<String> iterator = lists.iterator(); while (iterator.hasNext()){ String next = iterator.next(); if (next != null){ iterator.remove(); } } } }
- 多執行緒
- 使用java併發包下的類來代替對應的集合,如CopyOnWriteArrayList代替ArrayList,