<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          fail-safe 和 fail-fast 都是什么鬼?

          共 8353字,需瀏覽 17分鐘

           ·

          2021-09-07 10:36

          點(diǎn)擊關(guān)注公眾號,Java干貨及時(shí)送達(dá)

          你真的了解 fail-fastfail-safe 嗎???

          簡介

          java.util   包下的 屬于  fail-fast    , 快速失敗~ ??    
          java.util.concurrent   包下的 屬于  fail-safe   ,安全失敗~ ??
          簡單來說 就是    fail-fast   在迭代時(shí),如果發(fā)現(xiàn) 該集合數(shù)據(jù) 結(jié)構(gòu)被改變 (modCount != expectedModCount),就會(huì) 拋出 ConcurrentModificationException  
          小伙伴們可以參考下 下面的代碼簡單實(shí)驗(yàn)一下~ ??

          fail-fast 實(shí)驗(yàn)代碼

          實(shí)驗(yàn)對象是 Hashtable,這里采用 jdk1.7 的寫法 ~  
          因?yàn)椴┲鬟€在研究 下文中 ConcurrentHashMap 在7和8中有啥不一樣 ??
          class E implements Runnable{

              Hashtable<String, String> hashtable;

              public E(Hashtable<String, String> hashtable) {
                  this.hashtable = hashtable;
              }

              private void add(Hashtable<String, String> hashtable){
                  for (int i = 0; i < 10000000; i++) {
                      hashtable.put("a",""+i);
                  }
              }

              @Override
              public void run() {
                  add(hashtable);
              }
          }

          public class D {


              public static void main(String[] args) {
                  Hashtable<String, String> hashtable = new Hashtable<String, String>();
                  hashtable.put("1","2");
                  hashtable.put("2","2");
                  hashtable.put("3","2");
                  hashtable.put("4","2");
                  hashtable.put("15","2");


                  new Thread(new E(hashtable)).start();

                  Set<Map.Entry<String, String>> entries = hashtable.entrySet();
                  Iterator<Map.Entry<String, String>> iterator = entries.iterator();
                  try {
                      Thread.sleep(10);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  while (iterator.hasNext()){
                      System.out.println(iterator.next());
                      iterator.remove();
                  }
              }
          }

          效果如圖

          觸發(fā)的原理:


          當(dāng)集合數(shù)據(jù)結(jié)構(gòu)發(fā)生變化時(shí),這兩個(gè)值是不相等的,所以會(huì)拋出該異常~ 。

          結(jié)論:

          雖然 HashTable 是 線程安全的  , 但是它有  fail-fast 機(jī)制  ,所以在多線程情況下進(jìn)行 迭代 也不能去修改它的數(shù)據(jù)結(jié)構(gòu)!
          fail-fast 機(jī)制 不允許并發(fā)修改! 

          fail-safe  實(shí)驗(yàn)代碼


          class E implements Runnable{

              ConcurrentHashMap<String, String> concurrentHashMap;

              public E(ConcurrentHashMap<String, String> concurrentHashMap) {
                  this.concurrentHashMap = concurrentHashMap;
              }

              private void add( ConcurrentHashMap<String, String> concurrentHashMap){
                  for (int i = 0; i < 100000; i++) {
                      concurrentHashMap.put("a"+i,""+i);
                  }
              }

              @Override
              public void run() {
                  add(concurrentHashMap);
              }
          }
          public class D {



              public static void main(String[] args) {

                  ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<String, String>();

                  concurrentHashMap.put("1","2");
                  concurrentHashMap.put("2","2");
                  concurrentHashMap.put("3","2");
                  concurrentHashMap.put("4","2");
                  concurrentHashMap.put("15","2");

                  new Thread(new E(concurrentHashMap)).start();
                  try {
                      Thread.sleep(2);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }

                  Set<Map.Entry<String, String>> entries = concurrentHashMap.entrySet();


                  for (Map.Entry<String, String> entry : entries) {
                      System.out.println(entry);
          //            這里不用調(diào)用 iterator 去 remove
                      concurrentHashMap.remove(entry.getKey());
                  }

              }
          }

          效果如圖:

          代碼運(yùn)行講解,線程A 往里加數(shù)據(jù),線程B 遍歷它的數(shù)據(jù),并刪除。

          可以看到這里并沒有報(bào)錯(cuò)~,但是它也不能保證遍歷到所有的值 (可以理解為無法獲取到最新的值)
          有沒有感受到一絲絲 安全失敗的感覺~ ??

          哈哈哈 它的特點(diǎn)就是  ?? 允許并發(fā)修改,不會(huì)拋出 ConcurrentModificationException ,但是無法保證拿到的是最新的值


          不知道小伙伴們看完上面的實(shí)驗(yàn)代碼有沒有疑惑

          (???(???(???*)

          為什么可以調(diào)用它自身的 remove 呢?


          別急~ 我們先來看看使用這個(gè)迭代器中發(fā)生了什么?
          源碼走起~
          小伙伴們可以看看下面四張圖~
          創(chuàng)建迭代器的過程




          圖一 可以看到會(huì)去創(chuàng)造一個(gè) EntryIterator, 而 它又 繼承了 HashIterator ,在初始化時(shí),會(huì)先調(diào)用父類的構(gòu)造器。
          圖三 可以發(fā)現(xiàn)  HashIterator  在初始化 時(shí),會(huì)去調(diào)用 advance 方法 (這里就不展開這個(gè) concurrentHashMap結(jié)構(gòu)啦~ ) 這里的重點(diǎn)在最后一張圖 , 它調(diào)用的是 UNSAFE.getObjectVolatile 。

          它的作用是 強(qiáng)制從主存中獲取屬性值。 

          小伙伴們可以自行對比下 HashMap 或者 上面的 HashTable,他們都是直接 拿到代碼中定義的這個(gè) Entry[]~。??


          不知道小伙伴們 get 得到這個(gè)點(diǎn)沒有~  
          哈哈哈 容我嘮叨嘮叨一下~ ??

          4ye 在網(wǎng)上搜這個(gè) fail-fast 和  fail-safe 的區(qū)別時(shí),看到下面這張圖。
          幾乎都在說 fail-safe 會(huì)復(fù)制原來的集合,然后在復(fù)制出來的集合上進(jìn)行操作,然后就說這樣是不會(huì)拋出 ConcurrentModificationException 異常了。
          可是這種說法是 不嚴(yán)謹(jǐn)?shù)?/strong>~ ??  它描述的情況應(yīng)該是針對這個(gè)  CopyOnWriteArrayList  或者 CopyOnWriteArraySet 的情況(下面的源碼講到~)
          CopyOnWriteArrayList  源碼
          可以發(fā)現(xiàn)這里 snapshot 的指針是始終指向這個(gè)原數(shù)組的(當(dāng)你創(chuàng)建迭代器的時(shí)候)

          當(dāng)你添加數(shù)據(jù)時(shí),它會(huì)復(fù)制原來的數(shù)組,并在復(fù)制出來的數(shù)組上進(jìn)行修改,
          然后再設(shè)置進(jìn)去,可以發(fā)現(xiàn)至始至終都沒有修改到這個(gè)原數(shù)組,
          所以迭代器中的數(shù)據(jù)是不受影響的~??

          結(jié)論

          fail-safe  也是得具體情況具體分析的。
          1. 如果是  CopyOnWriteArrayList  或者 CopyOnWriteArraySet  ,就屬于 復(fù)制原來的集合,然后在復(fù)制出來的集合上進(jìn)行操作 的情況 ,所以是不會(huì)拋出這個(gè) ConcurrentModificationException  的 。

          2. 如果是這個(gè) concurrentHashMap 的,就比較硬核了~ ?? 它直接操作底層,調(diào)用UNSAFE.getObjectVolatile  ,直接  強(qiáng)制從主存中獲取屬性值,也是不會(huì)拋出這個(gè) ConcurrentModificationException  的 。

          3. 并發(fā)下,無法保證 遍歷時(shí)拿到的是最新的值~




          嘿嘿 現(xiàn)在回答上面那個(gè) 為啥可以 remove 的問題啦~

          remove 的源碼如下



          重點(diǎn)在紅框處, pred 為 null 表示是數(shù)組的頭部,此時(shí)調(diào)用 setEntryAt ,這里也是出現(xiàn)了這個(gè)  UNSAFE ??

          UNSAFE.putOrderedObject 這段代碼的意思就是 :
          有序的(有延遲的) 強(qiáng)制 更新數(shù)據(jù)到 主內(nèi)存。(不能立刻被其他線程發(fā)現(xiàn)) 
          這些 和 Java 的 JMM (Java內(nèi)存模型)有關(guān)!   

          總結(jié)

          java.util 包下的屬于fail-fast ,
          特點(diǎn):
          不允許并發(fā)修改,如果并發(fā)修改的話會(huì)導(dǎo)致在迭代過程中拋出 ConcurrentModificationException  ,
          觸發(fā)點(diǎn)是 modCount != expectedModCount??

          java.util.concurrent 包下的 屬于  fail-safe  ,
          特點(diǎn):
          允許并發(fā)修改,但是 無法保證在迭代過程中獲取到最新的值 。 ??  
          concurrentHashMap  獲取和修改數(shù)據(jù)時(shí) ,是通過 UNSAFE 類 直接從主內(nèi)存中獲取或者更新數(shù)據(jù)到主內(nèi)存~
          CopyOnWriteArrayList  或者 CopyOnWriteArraySet  ,就直接 復(fù)制原來的集合,然后在復(fù)制出來的集合上進(jìn)行操作






          關(guān)注Java技術(shù)??锤喔韶?/strong>



          獲取 Spring Boot 實(shí)戰(zhàn)筆記!
          瀏覽 60
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    色婷婷成人做爱a片免费看网站 | 国产一区二区大鸡巴操女女在线视频91 | 影音先锋成人A片 | 激情内射| 97精品国产手机 |