日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java - Java集合中的快速失败Fail Fast 机制

發(fā)布時(shí)間:2025/3/21 java 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java - Java集合中的快速失败Fail Fast 机制 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 什么是 fail-fast
  • 源碼解讀
    • Itr
    • 為什么對(duì)集合的結(jié)構(gòu)進(jìn)行修改會(huì)發(fā)生并發(fā)修改異常-源碼分析
      • 修改方法之 remove
      • 修改方法之 add
  • 案例分享
    • 【案例一】
    • 【案例二】
    • 【案例三】
    • 【案例四】
    • 【案例五】
    • 【案例六】
    • 【案例七】
  • 阿里巴巴Java開發(fā)手冊(cè)中的規(guī)定
  • 如何避免fail-fast拋異常


什么是 fail-fast

https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html

fail-fast 機(jī)制是Java集合(Collection)中的一種錯(cuò)誤機(jī)制。

在用迭代器遍歷一個(gè)集合對(duì)象時(shí),如果遍歷過程中對(duì)集合對(duì)象的結(jié)構(gòu)進(jìn)行了修改(增加、刪除),則會(huì)拋出Concurrent Modification Exception 【并發(fā)修改異常】。

舉個(gè)例子:

在多線程環(huán)境下,線程1正在對(duì)集合進(jìn)行遍歷,此時(shí)線程2對(duì)集合進(jìn)行修改 , 很容易拋出Concurrent Modification Exception 。

當(dāng)然了,在單線程的情況下,遍歷時(shí)對(duì)集合進(jìn)行修改也會(huì)拋出Concurrent Modification Exception

此類的返回的迭代器iterator和 listIterator方法是快速失敗的:如果列表在任何時(shí)間后,迭代器創(chuàng)建結(jié)構(gòu)修飾,以任何方式除非通過迭代器自身 remove或 add方法,迭代器都將拋出 Concurrent Modification Exception

因此,面對(duì)并發(fā)修改,迭代器快速而干凈地失敗,而不是冒著在未來不確定的時(shí)間出現(xiàn)任意、非確定性行為的風(fēng)險(xiǎn)。


源碼解讀

Itr

在遍歷的時(shí)候?qū)闲薷臅?huì)發(fā)生fail-fast,遍歷集合------> 迭代器

/*** An optimized version of AbstractList.Itr*/private class Itr implements Iterator<E> {int cursor; // index of next element to returnint lastRet = -1; // index of last element returned; -1 if no suchint expectedModCount = modCount;Itr() {}public boolean hasNext() {return cursor != size;}@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();}}@Override@SuppressWarnings("unchecked")public void forEachRemaining(Consumer<? super E> consumer) {Objects.requireNonNull(consumer);final int size = ArrayList.this.size;int i = cursor;if (i >= size) {return;}final Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length) {throw new ConcurrentModificationException();}while (i != size && modCount == expectedModCount) {consumer.accept((E) elementData[i++]);}// update once at end of iteration to reduce heap write trafficcursor = i;lastRet = i - 1;checkForComodification();}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}}

看到了吧, checkForComodification

final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}

在modCount != expectedModCount的時(shí)候拋出了ConcurrentModificationException,

而在next方法中上來就是調(diào)用checkForComodification,所以遍歷集合才會(huì)可能拋出并發(fā)修改異常。

那接下來就研究 modCount 和 expectedModCount 什么時(shí)候會(huì)不相等就行了唄。

  • 在創(chuàng)建一個(gè)迭代器后,expectedModCount的初始值就是modCount了,

  • 對(duì)集合修改會(huì)改變modCount,

  • expectedModCount只會(huì)在迭代器的remove方法中被修改為modCount

這都是 中的內(nèi)容,除了modCount 。 modCount 是ArrayList的常量,默認(rèn)值 為0


為什么對(duì)集合的結(jié)構(gòu)進(jìn)行修改會(huì)發(fā)生并發(fā)修改異常-源碼分析

那我們說,在用迭代器遍歷一個(gè)集合對(duì)象時(shí),如果遍歷過程中對(duì)集合對(duì)象的結(jié)構(gòu)進(jìn)行了修改(增加、刪除),則會(huì)拋出Concurrent Modification Exception 【并發(fā)修改異常】。

修改方法之 remove

modCount++ , 后面modCount會(huì)和expectedModCount不相等,進(jìn)而拋出并發(fā)修改異常。


修改方法之 add

ensureCapacityInternal方法里對(duì)modCount++操作, 改變了modCount的值,所以調(diào)用

那set方法會(huì)觸發(fā) fast fail嗎?

答案是不會(huì)。

set沒有對(duì)modCount++,所以對(duì)集合的某個(gè)元素進(jìn)行修改并不會(huì)fail-fast


案例分享

【案例一】

List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); Iterator<String> iter = list.iterator(); while (iter.hasNext()) {String tmp = iter.next();System.out.println(tmp);if (tmp.equals("1")) {list.remove("1");} } 1 Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)at java.util.ArrayList$Itr.next(ArrayList.java:861)at com.artisan.fastfail.FastFailTest.main(FastFailTest.java:25)

調(diào)用了 list# remove方法


【案例二】

List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); Iterator<String> iter = list.iterator(); while (iter.hasNext()) {String tmp = iter.next();System.out.println(tmp);if (tmp.equals("3")) {list.remove("3");} } 1 2 3

調(diào)用了 list# remove方法 , 居然沒有拋出并發(fā)修改異常????

remove倒數(shù)第二個(gè)元素,然而這時(shí)就沒有拋出異常了 。 再分析分析吧

cursor是下一個(gè)要返回的變量的下標(biāo)

lastRet是上一個(gè)返回過的變量的下標(biāo)

hasNext方法告訴我們只有在下一個(gè)變量的下標(biāo)不等于size的時(shí)候會(huì)告訴我們集合還有下一個(gè)元素。

但是在remove的時(shí)候,size- -了,那么刪除“3”這個(gè)元素后,size變?yōu)?,而此時(shí)cursor也是3,那么再走到hasNext時(shí),就發(fā)現(xiàn)cursor和size相等了,那么就會(huì)退出遍歷,“4”壓根就不會(huì)被遍歷到。

所以沒有拋出異常,因?yàn)閞emove后就退出了,還沒來得及走到next方法呢~


【案例三】

List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); Iterator<String> iter = list.iterator(); while (iter.hasNext()) {String tmp = iter.next();System.out.println(tmp);if (tmp.equals("4")) {list.remove("4");} } 1 2 3 4 Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)at java.util.ArrayList$Itr.next(ArrayList.java:861)at com.artisan.fastfail.FastFailTest.main(FastFailTest.java:25)

接上個(gè)案例

那 刪除“4”,也就是最后一個(gè)元素,按理說刪了最后一個(gè)元素不就退出了嗎?走不到下一次的next方法呀?

其實(shí)是不對(duì)的,刪完“4”并沒有就直接退出 ! remove后size變成了3,但此時(shí)cursor是4,那么走到hasNext時(shí),發(fā)現(xiàn)4!=3,就會(huì)再次進(jìn)入循環(huán),那么結(jié)果…走到了next方法,拋出了異常。。。


【案例四】

List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); for (String i : list) {if ("1".equals(i)) {list.remove("1");} } Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)at java.util.ArrayList$Itr.next(ArrayList.java:861)at com.artisan.fastfail.FastFailTest.main(FastFailTest.java:22)

用增強(qiáng)for循環(huán)遍歷的,反編譯class , 和用迭代器實(shí)質(zhì)是一樣的 。


【案例五】

List<String> list = Arrays.asList("1", "2", "3", "4"); for (String i : list) {if ("1".equals(i)) {list.remove("1");} } Exception in thread "main" java.lang.UnsupportedOperationExceptionat java.util.AbstractList.remove(AbstractList.java:161)at java.util.AbstractList$Itr.remove(AbstractList.java:374)at java.util.AbstractCollection.remove(AbstractCollection.java:293)at com.artisan.fastfail.FastFailTest.main(FastFailTest.java:21)

用了Array.asList()方法生成的集合,拋出的是UnsupportedOperationException,發(fā)現(xiàn)asList生成的ArrayList是個(gè)靜態(tài)內(nèi)部類,并非java.util.ArrayList, 并沒有這些方法。

所以不能對(duì)asList生成的ArrayList進(jìn)行增刪改

Java開發(fā)規(guī)范01 - 集合篇_Arrays.asList 坑


【案例六】

List<String> list = new ArrayList<>();list.add("1");list.add("2");list.add("3");list.add("4");Iterator<String> iter = list.iterator();while (iter.hasNext()) {String tmp = iter.next();System.out.println(tmp);if (tmp.equals("1")) {iter.remove();}} 1 2 3 4

【案例七】

```java // Java code to illustrate // Fail Fast Iterator in Java import java.util.HashMap; import java.util.Iterator; import java.util.Map;public class FailFastExample {public static void main(String[] args){Map<String, String> cityCode = new HashMap<String, String>();cityCode.put("Delhi", "India");cityCode.put("Moscow", "Russia");cityCode.put("New York", "USA");Iterator iterator = cityCode.keySet().iterator();while (iterator.hasNext()) {System.out.println(cityCode.get(iterator.next()));// adding an element to Map// exception will be thrown on next call// of next() method.cityCode.put("Istanbul", "Turkey");}} } India Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)at java.util.HashMap$KeyIterator.next(HashMap.java:1466)at FailFastExample.main(FailFastExample.java:18)

// Java code to demonstrate remove // case in Fail-fast iteratorsimport java.util.ArrayList; import java.util.Iterator;public class FailFastExample {public static void main(String[] args){ArrayList<Integer> al = new ArrayList<>();al.add(1);al.add(2);al.add(3);al.add(4);al.add(5);Iterator<Integer> itr = al.iterator();while (itr.hasNext()) {if (itr.next() == 2) {// will not throw Exceptionitr.remove();}}System.out.println(al);itr = al.iterator();while (itr.hasNext()) {if (itr.next() == 3) {// will throw Exception on// next call of next() methodal.remove(3);}}} } [1, 3, 4, 5] Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)at java.util.ArrayList$Itr.next(ArrayList.java:851)at FailFastExample.main(FailFastExample.java:28)



阿里巴巴Java開發(fā)手冊(cè)中的規(guī)定


如何避免fail-fast拋異常

  • 如果非要在遍歷的時(shí)候修改集合,那么建議用迭代器的remove等方法,而不是用集合的remove等方法
  • 并發(fā)的環(huán)境,那還要對(duì)Iterator對(duì)象加鎖, 也可以直接使用Collections.synchronizedList
  • CopyOnWriteArrayList(采用fail-safe)
  • 總結(jié)

    以上是生活随笔為你收集整理的Java - Java集合中的快速失败Fail Fast 机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。