两个迭代器的故事
當您查看最受歡迎的Java面試問題時,可能會遇到有關快速故障和故障安全迭代器的問題:
故障快速迭代器和故障安全迭代器之間有什么區別?
簡化的答案是:
如果在迭代過程中修改了集合,則快速失敗迭代器將引發ConcurrentModificationException ,但不會失敗保護。
盡管這完全有道理,但不清楚訪調員的故障安全含義。 對于迭代器,Java規范未定義此術語。 但是,有四種同時進行修改的策略。
并發修改
首先,讓我們定義什么是并發修改。 例如,當我們有一個來自集合的活動迭代器并且對該集合進行了一些更改,但它們不是來自我們的迭代器時,會發生并發修改。 最明顯的例子是當我們有多個線程時–一個線程正在迭代,第二個線程在同一集合中添加或刪除元素。 但是,當我們在單線程環境中工作時,我們也可以獲取ConcurrentModificationException :
List<String> cities = new ArrayList<>(); cities.add(“Warsaw”); cities.add(“Prague”); cities.add(“Budapest”);Iterator<String> cityIterator = cities.iterator(); cityIterator.next(); cities.remove(1); cityIterator.next(); // throws ConcurrentModificationException不及格
上面的代碼段是快速失敗迭代器的示例。 如您所見,一旦我們嘗試從迭代器中獲取第二個元素,就會引發ConcurrentModificationException 。 迭代器如何知道創建集合后是否對其進行了修改? 您可能在集合中有一個時間戳,例如lastModified 。 創建迭代器時,需要復制此字段并將其存儲在迭代器對象中。 然后,無論何時調用next()方法, lastModified需要將集合中的lastModified與迭代器中的副本進行比較。 例如,可以在ArrayList實現中找到非常相似的方法。 有一個modCount實例變量,其中包含對列表進行的修改次數:
final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException(); }值得一提的是,快速失敗迭代器是在盡力而為的基礎上工作的-無法保證如果存在并發修改,則會拋出ConcurrentModificationException ,因此我們不應該依賴該行為-而是應將其用于檢測錯誤。 大多數非并行集合都提供快速失敗的迭代器。
弱一致性
java.util.concurrent包中的大多數并發集合(例如ConcurrentHashMap和most Queues )都提供了弱一致性的迭代器。 文檔中對它的含義進行了很好的解釋:
- 他們可能會與其他操作同時進行
- 他們永遠不會拋出ConcurrentModificationException
- 它們被保證可以遍歷在構造時已經存在的元素一次,并且可以(但不保證)反映出構造后的任何修改。
快照
在此策略中,迭代器從創建迭代器的那一刻起即與集合的狀態相關聯-我們的集合快照。 對初始集合所做的任何更改都會創建基礎數據結構的新版本。 當然,我們的快照是不變的,因此它不反映在創建迭代器之后對集合所做的任何更改。 這是一種古老的好的寫時復制(COW)技術。 它完全解決了并發修改問題,因此不會引發ConcurrentModificationException 。 此外,迭代器不支持元素更改操作。 寫入時復制集合通常使用起來過于昂貴,但是如果突變發生的次數顯著減少(遍歷次數較少),嘗試一下可能是個好主意。 示例為CopyOnWriteArrayList和CopyOnWriteArraySet 。
未定義
未定義的行為可以在傳統集合中找到,例如Vector和Hashtables 。 它們都具有具有快速失敗行為的標準迭代器,但是它們還公開了Enumeration接口的實現,該接口在定義并發修改時不定義行為。 您可能會看到某些項目被重復或跳過,甚至出現一些奇怪的異常。 最好不要玩這個野獸!
翻譯自: https://www.javacodegeeks.com/2017/11/tale-two-iterators.html
總結
- 上一篇: 印度属于非洲吗(印度属于亚洲还是非洲国家
- 下一篇: 将文件拆分为流