fail-safe fail-fast知多少
文章目錄
- 簡(jiǎn)介
- Fail-fast Iterator
- Fail-fast 的原理
- Fail-safe Iterator
- 總結(jié)
fail-safe fail-fast知多少
簡(jiǎn)介
我們?cè)谑褂眉项惖臅r(shí)候,通常會(huì)需要去遍歷集合中的元素,并在遍歷中對(duì)其中的元素進(jìn)行處理。這時(shí)候我們就要用到Iterator,經(jīng)常寫程序的朋友應(yīng)該都知道,在Iterator遍歷的過程中,是不能夠修改集合數(shù)據(jù)的,否則就會(huì)拋出ConcurrentModificationException。
因?yàn)镃oncurrentModificationException的存在,就把Iterator分成了兩類,Fail-fast和Fail-safe。
Fail-fast Iterator
Fail-fast看名字就知道它的意思是失敗的非常快。就是說如果在遍歷的過程中修改了集合的結(jié)構(gòu),則就會(huì)立刻報(bào)錯(cuò)。
Fail-fast通常在下面兩種情況下拋出ConcurrentModificationException:
如果在單線程的環(huán)境中,iterator創(chuàng)建之后,如果不是通過iterator自身的remove方法,而是通過調(diào)用其他的方法修改了集合的結(jié)構(gòu),則會(huì)報(bào)錯(cuò)。
如果一個(gè)線程中創(chuàng)建了iterator,而在另外一個(gè)線程中修改了集合的結(jié)構(gòu),則會(huì)報(bào)錯(cuò)。
我們先看一個(gè)Fail-fast的例子:
Map<Integer,String> users = new HashMap<>();users.put(1, "jack");users.put(2, "alice");users.put(3, "jone");Iterator iterator1 = users.keySet().iterator();//not modify key, so no exceptionwhile (iterator1.hasNext()){log.info("{}",users.get(iterator1.next()));users.put(2, "mark");}上面的例子中,我們構(gòu)建了一個(gè)Map,然后遍歷該map的key,在遍歷過程中,我們修改了map的value。
運(yùn)行發(fā)現(xiàn),程序完美執(zhí)行,并沒有報(bào)任何異常。
這是因?yàn)槲覀儽闅v的是map的key,只要map的key沒有被手動(dòng)修改,就沒有問題。
再看一個(gè)例子:
Map<Integer,String> users = new HashMap<>();users.put(1, "jack");users.put(2, "alice");users.put(3, "jone");Iterator iterator1 = users.keySet().iterator();Iterator iterator2 = users.keySet().iterator();//modify key,get exceptionwhile (iterator2.hasNext()){log.info("{}",users.get(iterator2.next()));users.put(4, "mark");}上面的例子中,我們?cè)诒闅vmap的key的同時(shí),對(duì)key進(jìn)行了修改。這種情況下就會(huì)報(bào)錯(cuò)。
Fail-fast 的原理
為什么修改了集合的結(jié)構(gòu)就會(huì)報(bào)異常呢?
我們以ArrayList為例,來(lái)講解下Fail-fast 的原理。
在AbstractList中,定義了一個(gè)modCount變量:
protected transient int modCount = 0;在遍歷的過程中都會(huì)去調(diào)用checkForComodification()方法來(lái)對(duì)modCount進(jìn)行檢測(cè):
public E next() {checkForComodification();try {int i = cursor;E next = get(i);lastRet = i;cursor = i + 1;return next;} catch (IndexOutOfBoundsException e) {checkForComodification();throw new NoSuchElementException();}}如果檢測(cè)的結(jié)果不是所預(yù)期的,就會(huì)報(bào)錯(cuò):
final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}在創(chuàng)建Iterator的時(shí)候會(huì)復(fù)制當(dāng)前的modCount進(jìn)行比較,而這個(gè)modCount在每次集合修改的時(shí)候都會(huì)進(jìn)行變動(dòng),最終導(dǎo)致Iterator中的modCount和現(xiàn)有的modCount是不一致的。
public void set(E e) {if (lastRet < 0)throw new IllegalStateException();checkForComodification();try {AbstractList.this.set(lastRet, e);expectedModCount = modCount;} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}注意,Fail-fast并不保證所有的修改都會(huì)報(bào)錯(cuò),我們不能夠依賴ConcurrentModificationException來(lái)判斷遍歷中集合是否被修改。
Fail-safe Iterator
我們?cè)賮?lái)講一下Fail-safe,Fail-safe的意思是在遍歷的過程中,如果對(duì)集合進(jìn)行修改是不會(huì)報(bào)錯(cuò)的。
Concurrent包下面的類型都是Fail-safe的。看一個(gè)ConcurrentHashMap的例子:
Map<Integer,String> users = new ConcurrentHashMap<>();users.put(1, "jack");users.put(2, "alice");users.put(3, "jone");Iterator iterator1 = users.keySet().iterator();//not modify key, so no exceptionwhile (iterator1.hasNext()){log.info("{}",users.get(iterator1.next()));users.put(2, "mark");}Iterator iterator2 = users.keySet().iterator();//modify key,get exceptionwhile (iterator2.hasNext()){log.info("{}",users.get(iterator2.next()));users.put(4, "mark");}上面的例子完美執(zhí)行,不會(huì)報(bào)錯(cuò)。
總結(jié)
Fail-fast 和 Fail-safe 是集合遍歷的重要概念,希望大家能夠掌握。
本文的例子 https://github.com/ddean2009/learn-java-streams
更多精彩內(nèi)容且看:
- 區(qū)塊鏈從入門到放棄系列教程-涵蓋密碼學(xué),超級(jí)賬本,以太坊,Libra,比特幣等持續(xù)更新
- Spring Boot 2.X系列教程:七天從無(wú)到有掌握Spring Boot-持續(xù)更新
- Spring 5.X系列教程:滿足你對(duì)Spring5的一切想象-持續(xù)更新
- java程序員從小工到專家成神之路(2020版)-持續(xù)更新中,附詳細(xì)文章教程
歡迎關(guān)注我的公眾號(hào):程序那些事,更多精彩等著您!
更多內(nèi)容請(qǐng)?jiān)L問 www.flydean.com
總結(jié)
以上是生活随笔為你收集整理的fail-safe fail-fast知多少的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JDK14的新特性:Lombok的终结者
- 下一篇: Iterator to list的三种方