最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java你告訴我 fail-fast 是什么鬼

 更新時(shí)間:2019年12月27日 10:31:55   作者:沉默王二  
這篇文章主要介紹了Java你告訴我 fail-fast 是什么鬼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

01、前言

說起來真特么慚愧:十年 IT 老兵,Java 菜鳥一枚。今天我才了解到 Java 還有 fail-fast 一說。不得不感慨啊,學(xué)習(xí)真的是沒有止境。只要肯學(xué),就會(huì)有巨多巨多別人眼中的“舊”知識(shí)涌現(xiàn)出來,并且在我這全是新的。

能怎么辦呢?除了羞愧,就只能趕緊全身心地投入學(xué)習(xí),把這些知識(shí)掌握。

為了鎮(zhèn)樓,必須搬一段英文來解釋一下 fail-fast。

In systems design, a fail-fast system is one which immediately reports at its interface any condition that is likely to indicate a failure. Fail-fast systems are usually designed to stop normal operation rather than attempt to continue a possibly flawed process. Such designs often check the system's state at several points in an operation, so any failures can be detected early. The responsibility of a fail-fast module is detecting errors, then letting the next-highest level of the system handle them.

大家不嫌棄的話,我就用蹩腳的英語能力翻譯一下。某場(chǎng)戰(zhàn)役當(dāng)中,政委發(fā)現(xiàn)司令員在亂指揮的話,就立馬報(bào)告給權(quán)限更高的中央軍委——這樣可以有效地避免更嚴(yán)重的后果出現(xiàn)。當(dāng)然了,如果司令員是李云龍的話,報(bào)告也沒啥用。

不過,Java 的世界里不存在李云龍。fail-fast 扮演的就是政委的角色,一旦報(bào)告給上級(jí),后面的行動(dòng)就別想執(zhí)行。

怎么和代碼關(guān)聯(lián)起來呢?看下面這段代碼。

public void test(Wanger wanger) { 
 if (wanger == null) {
 throw new RuntimeException("wanger 不能為空");
 }
 
 System.out.println(wanger.toString());
}

一旦檢測(cè)到 wanger 為 null,就立馬拋出異常,讓調(diào)用者來決定這種情況下該怎么處理,下一步 wanger.toString() 就不會(huì)執(zhí)行了——避免更嚴(yán)重的錯(cuò)誤出現(xiàn),這段代碼由于太過簡(jiǎn)單,體現(xiàn)不出來,后面會(huì)講到。

瞧,fail-fast 就是這個(gè)鬼,沒什么神秘的。如果大家源碼看得比較多的話,這種例子多得就像旅游高峰期的人頭。

然后呢,沒了?三秒鐘,別著急,我們繼續(xù)。

02、for each 中集合的 remove 操作

很長(zhǎng)一段時(shí)間里,我都不明白為什么不能在 for each 循環(huán)里進(jìn)行元素的 remove。今天我們就來借機(jī)來體驗(yàn)一把。

List<String> list = new ArrayList<>();
list.add("沉默王二");
list.add("沉默王三");
list.add("一個(gè)文章真特么有趣的程序員");

for (String str : list) {
 if ("沉默王二".equals(str)) {
 list.remove(str);
 }
}

System.out.println(list);

這段代碼看起來沒有任何問題,但運(yùn)行起來就糟糕了。

Exception in thread "main" java.util.ConcurrentModificationException
 at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
 at java.util.ArrayList$Itr.next(ArrayList.java:859)
 at com.cmower.java_demo.str.Cmower3.main(Cmower3.java:14)

為毛呢?

03、分析問題的殺手锏

這時(shí)候就只能看源碼了,ArrayList.java 的 909 行代碼是這樣的。

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

也就是說,remove 的時(shí)候執(zhí)行了 checkForComodification 方法,該方法對(duì) modCount 和 expectedModCount 進(jìn)行了比較,發(fā)現(xiàn)兩者不等,就拋出了 ConcurrentModificationException 異常。

可為什么會(huì)執(zhí)行 checkForComodification 方法呢?這就需要反編譯一下 for each 那段代碼了。

List<String> list = new ArrayList();
list.add("沉默王二");
list.add("沉默王三");
list.add("一個(gè)文章真特么有趣的程序員");
Iterator var3 = list.iterator();

while (var3.hasNext()) {
 String str = (String) var3.next();
 if ("沉默王二".equals(str)) {
 list.remove(str);
 }
}

System.out.println(list);

原來 for each 是通過迭代器 Iterator 配合 while 循環(huán)實(shí)現(xiàn)的。

1)ArrayList.iterator() 返回的 Iterator 其實(shí)是 ArrayList 的一個(gè)內(nèi)部類 Itr。

public Iterator<E> iterator() {
  return new Itr();
}

Itr 實(shí)現(xiàn)了 Iterator 接口。

private class Itr implements Iterator<E> {
  int cursor;    // index of next element to return
  int lastRet = -1; // index of last element returned; -1 if no such
  int expectedModCount = modCount;

  Itr() {}

  public boolean hasNext() {
    return cursor != size;
  }

  @SuppressWarnings("unchecked")
  public E next() {
    checkForComodification();
    int i = cursor;
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
      throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
  }
}

也就是說 new Itr() 的時(shí)候 expectedModCount 被賦值為 modCount,而 modCount 是 List 的一個(gè)成員變量,表示集合被修改的次數(shù)。由于 list 此前執(zhí)行了 3 次 add 方法,所以 modCount 的值為 3;expectedModCount 的值也為 3。

可當(dāng)執(zhí)行 list.remove(str) 后,modCount 的值變成了 4。

private void fastRemove(int index) {
  modCount++;
  int numMoved = size - index - 1;
  if (numMoved > 0)
    System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
  elementData[--size] = null; // clear to let GC do its work
}

注:remove 方法內(nèi)部調(diào)用了 fastRemove 方法。

下一次循環(huán)執(zhí)行到 String str = (String) var3.next(); 的時(shí)候,就會(huì)調(diào)用 checkForComodification 方法,此時(shí)一個(gè)為 3,一個(gè)為

4,就只好拋出異常 ConcurrentModificationException 了。

不信,可以直接在 ArrayList 類的 909 行打個(gè)斷點(diǎn) debug 一下。

真的耶,一個(gè)是 4 一個(gè)是 3。

總結(jié)一下。在 for each 循環(huán)中,集合遍歷其實(shí)是通過迭代器 Iterator 配合 while 循環(huán)實(shí)現(xiàn)的,但是元素的 remove 卻直接使用的集合類自身的方法。這就導(dǎo)致 Iterator 在遍歷的時(shí)候,會(huì)發(fā)現(xiàn)元素在自己不知情的情況下被修改了,它覺得很難接受,就拋出了異常。

讀者朋友們,你們是不是覺得我跑題了,fail-fast 和 for each 中集合的 remove 操作有什么關(guān)系呢?

有!Iterator 使用了 fail-fast 的保護(hù)機(jī)制。

04、怎么避開 fail-fast 保護(hù)機(jī)制呢

通過上面的分析,相信大家都明白為什么不能在 for each 循環(huán)里進(jìn)行元素的 remove 了。

那怎么避開 fail-fast 保護(hù)機(jī)制呢?畢竟刪除元素是常規(guī)操作,咱不能因噎廢食啊。

1)remove 后 break

List<String> list = new ArrayList<>();
list.add("沉默王二");
list.add("沉默王三");
list.add("一個(gè)文章真特么有趣的程序員");

for (String str : list) {
 if ("沉默王二".equals(str)) {
 list.remove(str);
 break;
 }
}

我怎么這么聰明,忍不住驕傲一下。有讀者不明白為什么嗎?那我上面的源碼分析可就白分析了,爬樓再看一遍吧!

略微透露一下原因:break 后循環(huán)就不再遍歷了,意味著 Iterator 的 next 方法不再執(zhí)行了,也就意味著 checkForComodification 方法不再執(zhí)行了,所以異常也就不會(huì)拋出了。

但是呢,當(dāng) List 中有重復(fù)元素要?jiǎng)h除的時(shí)候,break 就不合適了。

2)for 循環(huán)

List<String> list = new ArrayList<>();
list.add("沉默王二");
list.add("沉默王三");
list.add("一個(gè)文章真特么有趣的程序員");
for (int i = 0, n = list.size(); i < n; i++) {
 String str = list.get(i);
 if ("沉默王二".equals(str)) {
 list.remove(str);
 }
} 

for 循環(huán)雖然可以避開 fail-fast 保護(hù)機(jī)制,也就說 remove 元素后不再拋出異常;但是呢,這段程序在原則上是有問題的。為什么呢?

第一次循環(huán)的時(shí)候,i 為 0,list.size() 為 3,當(dāng)執(zhí)行完 remove 方法后,i 為 1,list.size() 卻變成了 2,因?yàn)?list 的大小在 remove 后發(fā)生了變化,也就意味著“沉默王三”這個(gè)元素被跳過了。能明白嗎?

remove 之前 list.get(1) 為“沉默王三”;但 remove 之后 list.get(1) 變成了“一個(gè)文章真特么有趣的程序員”,而 list.get(0) 變成了“沉默王三”。

3)Iterator

List<String> list = new ArrayList<>();
list.add("沉默王二");
list.add("沉默王三");
list.add("一個(gè)文章真特么有趣的程序員");

Iterator<String> itr = list.iterator();

while (itr.hasNext()) {
 String str = itr.next();
 if ("沉默王二".equals(str)) {
 itr.remove();
 }
}

為什么使用 Iterator 的 remove 方法就可以避開 fail-fast 保護(hù)機(jī)制呢?看一下 remove 的源碼就明白了。

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();
  }
}

雖然刪除元素依然使用的是 ArrayList 的 remove 方法,但是刪除完會(huì)執(zhí)行 expectedModCount = modCount,保證了 expectedModCount 與 modCount 的同步。

05、最后

在 Java 中,fail-fast 從狹義上講是針對(duì)多線程情況下的集合迭代器而言的。這一點(diǎn)可以從 ConcurrentModificationException 定義上看得出來。

This exception may be thrown by methods that have detected concurrent
modification of an object when such modification is not permissible.

For example, it is not generally permissible for one thread to modify a Collectionwhile another thread is iterating over it. In general, the results of theiteration are undefined under these circumstances. Some Iteratorimplementations (including those of all the general purpose collection implementationsprovided by the JRE) may choose to throw this exception if this behavior isdetected. Iterators that do this are known as fail-fast iterators,as they fail quickly and cleanly, rather that risking arbitrary,non-deterministic behavior at an undetermined time in the future.

再次拙劣地翻譯一下。

該異常可能由于檢測(cè)到對(duì)象在并發(fā)情況下被修改而拋出的,而這種修改是不允許的。

通常,這種操作是不允許的,比如說一個(gè)線程在修改集合,而另一個(gè)線程在迭代它。這種情況下,迭代的結(jié)果是不確定的。如果檢測(cè)到這種行為,一些 Iterator(比如說 ArrayList 的內(nèi)部類 Itr)就會(huì)選擇拋出該異常。這樣的迭代器被稱為 fail-fast 迭代器,因?yàn)楸M早的失敗比未來出現(xiàn)不確定的風(fēng)險(xiǎn)更好。

既然是針對(duì)多線程,為什么我們之前的分析都是基于單線程的呢?因?yàn)閺膹V義上講,fail-fast 指的是當(dāng)有異?;蛘咤e(cuò)誤發(fā)生時(shí)就立即中斷執(zhí)行的這種設(shè)計(jì),從單線程的角度去分析,大家更容易明白。

你說對(duì)嗎?

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • java教學(xué)筆記之對(duì)象的創(chuàng)建與銷毀

    java教學(xué)筆記之對(duì)象的創(chuàng)建與銷毀

    面向?qū)ο蟮木幊陶Z言使程序能夠直觀的反應(yīng)客觀世界的本來面目,并且使軟件開發(fā)人員能夠運(yùn)用人類認(rèn)識(shí)事物所采用的一般思維方法進(jìn)行軟件開發(fā),是當(dāng)今計(jì)算機(jī)領(lǐng)域中軟件開發(fā)和應(yīng)用的主流技術(shù)。
    2016-01-01
  • Java異常處理 如何跟蹤異常的傳播路徑

    Java異常處理 如何跟蹤異常的傳播路徑

    這篇文章主要介紹了Java異常處理 如何跟蹤異常的傳播路徑,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • idea中如何全局搜索class文件或者字符串

    idea中如何全局搜索class文件或者字符串

    這篇文章主要介紹了idea中如何實(shí)現(xiàn)全局搜索class文件或者字符串問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • SpringBoot整合Elasticsearch7.2.0的實(shí)現(xiàn)方法

    SpringBoot整合Elasticsearch7.2.0的實(shí)現(xiàn)方法

    這篇文章主要介紹了SpringBoot整合Elasticsearch7.2.0的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • 淺析java創(chuàng)建文件和目錄

    淺析java創(chuàng)建文件和目錄

    這篇文章主要介紹了淺析java創(chuàng)建文件和目錄的關(guān)鍵技術(shù)點(diǎn)以及演示示例,是篇非常不錯(cuò)的文章,有需要的朋友可以參考下
    2014-09-09
  • spring依賴注入知識(shí)點(diǎn)分享

    spring依賴注入知識(shí)點(diǎn)分享

    在本篇文章里小編給大家整理的是關(guān)于spring依賴注入知識(shí)點(diǎn)以及相關(guān)代碼內(nèi)容,需要的朋友們學(xué)習(xí)下。
    2019-11-11
  • 你應(yīng)該知道的這些Mybatis-Plus使用技巧(小結(jié))

    你應(yīng)該知道的這些Mybatis-Plus使用技巧(小結(jié))

    這篇文章主要介紹了你應(yīng)該知道的這些Mybatis-Plus使用技巧(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Spring?IOC容器使用詳細(xì)講解

    Spring?IOC容器使用詳細(xì)講解

    IOC-Inversion?of?Control,即控制反轉(zhuǎn)。它不是什么技術(shù),而是一種設(shè)計(jì)思想。這篇文章將為大家介紹一下Spring控制反轉(zhuǎn)IOC的原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-12-12
  • Spring中的BeanFactory與FactoryBean區(qū)別詳解

    Spring中的BeanFactory與FactoryBean區(qū)別詳解

    這篇文章主要介紹了Spring中的BeanFactory與FactoryBean區(qū)別詳解,BeanFactory是一個(gè)接口,它是spring中的一個(gè)工廠,FactoryBean也是一個(gè)接口,實(shí)現(xiàn)了3個(gè)方法,通過重寫其中方法自定義生成bean,需要的朋友可以參考下
    2024-01-01
  • SpringBoot集成Auth0 JWT的示例代碼

    SpringBoot集成Auth0 JWT的示例代碼

    本文主要介紹了SpringBoot集成Auth0 JWT的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08

最新評(píng)論

棋牌| 桃江县| 镶黄旗| 玛曲县| 崇明县| 鹤庆县| 越西县| 武胜县| 和平区| 额尔古纳市| 诏安县| 临沭县| 嘉义县| 平度市| 陆丰市| 锦屏县| 旬邑县| 剑阁县| 佳木斯市| 拜泉县| 浦江县| 万年县| 云和县| 临漳县| 苗栗县| 湖南省| 桦南县| 古蔺县| 周口市| 安庆市| 盐池县| 井研县| 金秀| 江陵县| 杂多县| 东海县| 都江堰市| 正镶白旗| 晋城| 鄄城县| 文山县|