通过ArrayList对modCount的操作分析fail-fast 机制
AbstractList類中有一個屬性
protected transient int modCount = 0;
api中對它的描述是:
- 此列表已被結構修改的次數。 結構修改是改變列表大小的那些修改,或以其他方式擾亂它,使得正在進行的迭代可能產生不正確的結果。
- 該字段由迭代器和列表迭代器實現使用,由iterator和listIterator方法返回。 如果該字段的值意外更改,迭代器(或列表迭代器)將拋出一個ConcurrentModificationException響應next?,?remove?,?previous?,?set或add操作。 這提供了fail-fast行為,而不是面對在迭代期間的并發修改的非確定性行為
我的理解是,modCount表示了當前列表結構被修改的次數,在調用迭代器操作時,則會檢查這個值,如果發現已更改,拋出異常
不過需要注意的是transient修飾意味著這個屬性不會被序列化,而且modCount并沒有被voliate修飾,也就是它不能保證在線程之間是可見的
?
從ArraytList源碼中可以發現,add,remove,clear等方法實現時,均添加了modCount++;操作,例如clear方法:
public void clear() {modCount++;// clear to let GC do its workfor (int i = 0; i < size; i++)elementData[i] = null;size = 0;}在arraylist中調用迭代器是通過內部類實現的:
public Iterator<E> iterator() {return new Itr();}在這個內部類中,同樣維護了一個類似modCount的變量
int expectedModCount = modCount;
并提供了檢測方法
final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}這個檢測方法在迭代器中類似next方法里面作為首先需要判斷的條件
@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];}我們綜合以上,就可以得出,
在使用迭代器遍歷arraylist時,會初始化一個和modCount相等的變量,如果在迭代過程中,arraylist中發生了類似add這種改變結構的操作(modCount改變),導致modCount != expectedModCount,那么會拋出一個異常ConcurrentModificationException,即產生fail-fast事件
?
產生fail-fast有兩種原因:
1.單線程情況下,迭代的過程中,調用類似add方法,但是一般不會這樣做
ArrayList<Integer> al = new ArrayList<>();for(int i=0;i<10;i++)al.add(i);Iterator<Integer> it = al.iterator();while(it.hasNext()) {System.out.println(it.next());if(!it.hasNext())al.add(10);}?
2.主要發生在多線程情況下,例如讓線程1迭代,線程2修改,就有可能會出現
final ArrayList<Integer> al = new ArrayList<>();for(int i=0;i<10;i++)al.add(i); new Thread(new Runnable() { @Overridepublic void run() {Iterator<Integer> it = al.iterator();while(it.hasNext()) {System.out.print(it.next()+" ");} }}).start(); new Thread(new Runnable() { @Overridepublic void run() {al.remove(6); } }).start();?
如果拋出異常,就類似這個樣子:
0 1 Exception in thread "Thread-0" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)at java.util.ArrayList$Itr.next(ArrayList.java:859)at com.rw.importword.utils.Text$1.run(Text.java:19)at java.lang.Thread.run(Thread.java:748)?
但是也有可能不拋出異常:
?
根據輸出結果來看,al的結構已經改變,但是沒有拋出異常
至于原因我想是:
modCount沒有被voliate修飾,在線程之間不可見,可能某一個時機,線程2中remove操作改變的modCount值并沒有及時寫到內存中,線程1中迭代器獲取的modCount值仍然是之前的值
?
由此可以得出,fail-fast機制,是一種錯誤檢測機制。它只能被用來檢測錯誤,因為JDK并不保證fail-fast機制一定會發生。
?
轉載于:https://www.cnblogs.com/NextLight/p/9592069.html
總結
以上是生活随笔為你收集整理的通过ArrayList对modCount的操作分析fail-fast 机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【一步一步学习spring】spring
- 下一篇: requests不加代理