發(fā)現(xiàn)前面博客遺失了一篇ArrayList源碼解析的文章,這里就不打算重寫了,但是其中關(guān)于Fail-Fast機制的知識還是有必要記錄一下。
Fail-Fast機制
集合類數(shù)據(jù)類型都有一個迭代器用于遍歷集合,這樣的設(shè)計是為了能使用共同的方式迭代集合,而不依賴于集合的類型的具體實現(xiàn),比如無論哪種集合類型,都能使用foreach迭代。
但是在這個迭代過程中,我們需要防止通過原集合的提供的api對集合進行修改。因為這可能導致迭代結(jié)果不符合預期,或者是出現(xiàn)角標越界等問題。
- 不符合預期的情況:在迭代過程中,往迭代的位置前插入元素,那么這個元素不會被迭代到。如果修改已經(jīng)迭代過的元素,那么迭代結(jié)果也和當前集合不一致。
- 角標越界的情況:迭代過程中,刪除元素,迭代器游標可能超過了集合的長度。
所以在集合的基類中就定義了modCount字段,每當調(diào)用會對集合數(shù)據(jù)結(jié)構(gòu)產(chǎn)生變化的方法時,modCount++。
而迭代器中則定義了expectCount字段,在迭代器創(chuàng)建時,expectCount賦值為modCount,當?shù)鷷r出現(xiàn)expectCount不能與modCount時就會拋出ConcurrentModifiedException,這就是所謂的Fail-Fast機制:在迭代過程中可能出現(xiàn)錯誤的時候,提前拋出并發(fā)修改異常,中斷迭代操作。
當然,自己實現(xiàn)集合的子類也可以忽略這個字段,不需要Fail-Fast機制。在modCount字段中上面有這么一段文檔注釋:
If an implementation does not wish to provide fail-fast iterators, this field may be ignored.
容易誤解的地方
我看有篇博文說modCount是被volatile修飾的,然后這個小伙伴就將Fail-Fast機制和多線程一起巴拉巴拉的說了一堆……我翻完了jdk各個版本的源碼都沒發(fā)現(xiàn)這個修飾,估計是將transition誤以為volatile了。但其實即使是volatile也無法保證modCount線程安全,因為modCount++并不是一個原子性操作……
有的人覺得Fail-Fast機制和多線程有關(guān)、modCount是為了多線程設(shè)計的,這是很嚴重的錯誤的理解。
并不是多線程才會拋出并發(fā)修改異常的,而且這個機制也不是針對多線程設(shè)計的,modCount對多線程的意義不大,因為主線程中modCount++后,子線程讀取到的可能還是自增前的數(shù)值,所以多線程情況下反而可能不拋出ConcurrentModifiedException了,導致Fail-Fast機制失效。
Fail-Safe
在線程安全的集合中,迭代器是拷貝集合的副本進行迭代的,所以即使在迭代過程中修改原集合,也不會出現(xiàn)角標越界的情況。但相對的,會出現(xiàn)不符合預期的情況,此時迭代結(jié)果和原集合內(nèi)容已經(jīng)不一致。
所以這就是Fail-Safe命名的由來,安全的錯誤。