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

Java集合快速失敗與安全失敗解析

 更新時間:2021年11月16日 11:39:06   作者:蹊源的奇思妙想  
這篇文章主要介紹了Java集合快速失敗與安全失敗解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

Java集合快速失敗與安全失敗

前言

我們在開發(fā)過程中有沒有在遍歷集合的時候遇到過ConcurrentModificationException這樣的異常,那么什么樣的原因導致這種異常呢?本篇博客將帶領大家去了解一下Java集合fail-fast快速失敗機制與fail-safe安全失敗機制。

正文

fail-fast與fail-safe

  • fail-fast快速失敗機制: 是Java集合中的一種機制,在用迭代器遍歷一個集合對象時,如果遍歷過程中對集合對象的內容進行了修改(增加、刪除、修改),則會拋出ConcurrentModificationException。
  • fail-safe安全失敗機制:java.util.concurrent包下的容器都是安全失敗,在遍歷時不是直接在集合內容上訪問的,而是先copy原有集合內容,在拷貝的集合上進行遍歷,因此采用安全失敗的容器可以在多線程下并發(fā)使用,并發(fā)修改。

fail-fast快速失敗機制

public class test {
    public static void main(String[] args) {
        testForHashMap();
    }
    private static void testForHashMap() {
        HashMap<String,String> hashMap =new LinkedHashMap<>();
        hashMap.put("1","a");
        hashMap.put("2","b");
        hashMap.put("3","c");
        Iterator<Map.Entry<String,String>> iterator=hashMap.entrySet().iterator();
        while (iterator.hasNext()) {
            hashMap.put("bloom","bloom");
            System.out.println(iterator.next());
        }
    }
}

快速失敗機制下修改集合元素觸發(fā)快速失敗,輸出結果:

遍歷集合時,新增或者刪除元素,將拋ConcurrentModificationException異常

在這里插入圖片描述

fail-safe安全失敗機制

public class test {   
    public static void main(String[] args) {
        testForHashTable();
    }
    private static void testForHashTable() {
        Hashtable<String,String> hashtable =new Hashtable();
        hashtable.put("4","d");
        hashtable.put("5","e");
        hashtable.put("6","f");
        Enumeration<String> iterator1=hashtable.elements();
        while (iterator1.hasMoreElements()) {
            hashtable.put("bloom","bloom");
            System.out.println(iterator1.nextElement());
        }
    }
}

安全失敗機制下修改集合元素,輸出結果

我們可以在遍歷集合的同時,新增、刪除元素

在這里插入圖片描述

小結一下

fail-fast,它是Java集合的一種錯誤檢測機制。

在用迭代器遍歷一個集合對象時,如果遍歷過程中不應該對集合對象的內容進行了修改(增加、刪除、修改),可以新建一個新的集合進行操作。

快速失敗&安全失?。ㄗ钊目偨Y)

public static void main(String[] args) {
Hashtable<String, String> table = new Hashtable<String, String>();
table.put("a", "aa");
table.put("b", "bb");
table.put("c", "cc");
table.remove("c");
Iterator<Entry<String, String>> iterator = table.entrySet().iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().getValue());
//采用iterator直接進行修改 程序正常
iterator.remove();
//直接從hashtable增刪數(shù)據就會報錯
table.put("d", "dd");
//直接從hashtable增刪數(shù)據就會報錯,hashtable,hashmap等非并發(fā)集合,如果在迭代過程中增減了數(shù)據,就是快速失敗
table.remove("c");
}
System.out.println("-----------");
Lock lock = new ReentrantLock();
//即使加上lock,還是會跑出ConcurrentModificationException異常
lock.lock();
HashMap<String, String> hashmap = new HashMap<String, String>();
hashmap.put("a", "aa");
hashmap.put("b", "bb");
hashmap.put("c", "cc");
Iterator<Entry<String, String>> iterators = hashmap.entrySet().iterator();
while (iterators.hasNext()) {
System.out.println(iterators.next().getValue());
// 正常
iterators.remove();
//直接從hashtable增刪數(shù)據就會報錯。
//hashtable,hashmap等非并發(fā)集合,如果在迭代過程中增減了數(shù)據,會快速失敗 (一檢測到修改,馬上拋異常) 
//java.util.ConcurrentModificationException
hashmap.remove("c");
}
System.out.println("-----------");
lock.unlock();
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();
map.put("a", "aa");
map.put("b", "bb");
map.put("c", "cc");
Iterator<Entry<String, String>> mapiterator = map.entrySet().iterator();
while (mapiterator.hasNext()) {
System.out.println(mapiterator.next().getValue());
map.remove("c");// 正常 并發(fā)集合不存在快速失敗問題
map.put("c", "cc");// 正常 并發(fā)集合不存在快速失敗問題
}
System.out.println("-----------");
}

運行該段代碼發(fā)現(xiàn),在Hashtable和HashMap的循環(huán)迭代過程中在容器對象上做“修改”操作的話,是跑出java.util.ConcurrentModificationException異常,在Iterator上做操作不會異常。但是ConcurrentHashMap在容器對象和Iterator對象上都不會拋異常,這是為什么呢?

(1)首先來介紹兩個概念,快速失敗和安全失敗。

Iterator的安全失敗是基于對底層集合做拷貝,因此,它不受源集合上修改的影響。java.util包下面的所有的集合類都是快速失敗的,而java.util.concurrent包下面的所有的類都是安全失敗的。

快速失敗的迭代器會拋出ConcurrentModificationException異常,而安全失敗的迭代器永遠不會拋出這樣的異常。

(2)我們查看Hashtable、HashMap、ConcurrentHashMap的在Java API底層的entrySet對象發(fā)現(xiàn),三者都做了對當前對象的拷貝,三者的處理方式是一樣的,那區(qū)別在哪里呢?看看獲取下一個entrySet在邏輯上的區(qū)別

這是Hashtable、HashMap的

final Node<K,V> nextNode() {
            Node<K,V>[] t;
            Node<K,V> e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            if ((next = (current = e).next) == null && (t = table) != null) {
                do {} while (index < t.length && (next = t[index++]) == null);
            }
            return e;
        }

這是ConcurrentHashMap的

public final Map.Entry<K,V> next() {
            Node<K,V> p;
            if ((p = next) == null)
                throw new NoSuchElementException();
            K k = p.key;
            V v = p.val;
            lastReturned = p;
            advance();
            return new MapEntry<K,V>(k, v, map);
        }
/**
         * Advances if possible, returning next valid node, or null if none.
         */
        final Node<K,V> advance() {
            Node<K,V> e;
            if ((e = next) != null)
                e = e.next;
            for (;;) {
                Node<K,V>[] t; int i, n;  // must use locals in checks
                if (e != null)
                    return next = e;
                if (baseIndex >= baseLimit || (t = tab) == null ||
                    (n = t.length) <= (i = index) || i < 0)
                    return next = null;
                if ((e = tabAt(t, i)) != null && e.hash < 0) {
                    if (e instanceof ForwardingNode) {
                        tab = ((ForwardingNode<K,V>)e).nextTable;
                        e = null;
                        pushState(t, i, n);
                        continue;
                    }
                    else if (e instanceof TreeBin)
                        e = ((TreeBin<K,V>)e).first;
                    else
                        e = null;
                }
                if (stack != null)
                    recoverState(n);
                else if ((index = i + baseSize) >= n)
                    index = ++baseIndex; // visit upper slots if present
            }
        }

ConcurrentHashMap中的迭代器主要包括entrySet、keySet、values方法。它們大同小異,這里選擇entrySet解釋。當我們調用entrySet返回值的iterator方法時,返回的是EntryIterator,在EntryIterator上調用next方法時,最終實際調用到了HashIterator.advance()方法。這個方法在遍歷底層數(shù)組。

在遍歷過程中,如果已經遍歷的數(shù)組上的內容變化了,迭代器不會拋出ConcurrentModificationException異常。如果未遍歷的數(shù)組上的內容發(fā)生了變化,則有可能反映到迭代過程中。

這就是ConcurrentHashMap迭代器弱一致的表現(xiàn)。ConcurrentHashMap的弱一致性主要是為了提升效率,是一致性與效率之間的一種權衡。要成為強一致性,就得到處使用鎖,甚至是全局鎖,這就與Hashtable和同步的HashMap一樣了。

最后我們看看JDK中對于快速失敗的描述:

注意,此實現(xiàn)不是同步的。如果多個線程同時訪問一個哈希映射,而其中至少一個線程從結構上修改了該映射,則它必須 保持外部同步。(結構上的修改是指添加或刪除一個或多個映射關系的任何操作;僅改變與實例已經包含的鍵關聯(lián)的值不是結構上的修改。)這一般通過對自然封裝該映射的對象進行同步操作來完成。

如果不存在這樣的對象,則應該使用 Collections.synchronizedMap 方法來“包裝”該映射。最好在創(chuàng)建時完成這一操作,以防止對映射進行意外的非同步訪問,如下所示: Map m = Collections.synchronizedMap(new HashMap(...));由所有此類的“collection 視圖方法”所返回的迭代器都是快速失敗 的:在迭代器創(chuàng)建之后,如果從結構上對映射進行修改,除非通過迭代器本身的 remove 方法,其他任何時間任何方式的修改,迭代器都將拋出 ConcurrentModificationException。因此,面對并發(fā)的修改,迭代器很快就會完全失敗,而不冒在將來不確定的時間發(fā)生任意不確定行為的風險。

注意,迭代器的快速失敗行為不能得到保證,一般來說,存在非同步的并發(fā)修改時,不可能作出任何堅決的保證??焖偈〉鞅M最大努力拋出 ConcurrentModificationException。因此,編寫依賴于此異常的程序的做法是錯誤的,正確做法是:迭代器的快速失敗行為應該僅用于檢測程序錯誤。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • SpringBoot使用MockMvc進行單元測試的實例代碼

    SpringBoot使用MockMvc進行單元測試的實例代碼

    在Spring Boot應用程序中,使用MockMvc進行單元測試是一種有效的方式,可以驗證控制器的行為和API的正確性,在這篇博客中,我們將介紹如何使用MockMvc對用戶控制器進行測試,感興趣的朋友可以參考下
    2024-01-01
  • jdbc和mybatis的流式查詢使用方法

    jdbc和mybatis的流式查詢使用方法

    有些時候我們所需要查詢的數(shù)據量比較大,但是jvm內存又是有限制的,數(shù)據量過大會導致內存溢出。這個時候就可以使用流式查詢,本文就主要介紹了jdbc和mybatis的流式查詢,感興趣的可以了解一下
    2021-11-11
  • javaWeb實現(xiàn)簡單文件上傳

    javaWeb實現(xiàn)簡單文件上傳

    這篇文章主要為大家詳細介紹了JAVAWeb實現(xiàn)簡單文件上傳,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • idea啟動springmvc項目時報找不到類的解決方法

    idea啟動springmvc項目時報找不到類的解決方法

    這篇文章主要介紹了idea啟動springmvc項目時報找不到類的解決方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • java如何用反射將一個對象復制給另一個對象

    java如何用反射將一個對象復制給另一個對象

    這篇文章主要介紹了java如何用反射將一個對象復制給另一個對象問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • 教你怎么使用Optional處理null

    教你怎么使用Optional處理null

    今天教各位小伙伴怎么使用Optional處理null,文中有非常詳細的代碼示例,對正在學習java的小伙伴們有很大的幫助,需要的朋友可以參考下
    2021-05-05
  • java安全編碼指南之:Mutability可變性詳解

    java安全編碼指南之:Mutability可變性詳解

    這篇文章主要介紹了java安全編碼指南之:Mutability可變性詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • IDEA 配置 JRebel 熱部署的方法(推薦)

    IDEA 配置 JRebel 熱部署的方法(推薦)

    這篇文章主要介紹了IDEA 配置 JRebel 熱部署的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01
  • SpringBoot集成redisson全過程

    SpringBoot集成redisson全過程

    本文主要介紹了如何集成Redisson,包括環(huán)境配置、引入Redisson的依賴、添加Redisson的配置類以及Redisson的常見使用方法,在類中注入Redisson后,可以獲取鎖對象和使用,這些都是作者的個人經驗,供讀者參考
    2024-10-10
  • java操作hdfs的方法示例代碼

    java操作hdfs的方法示例代碼

    這篇文章主要介紹了java操作hdfs的相關資料,在本地配置Hadoop和Maven的環(huán)境變量,首先需從官網下載與服務器相同版本的Hadoop安裝包,配置環(huán)境變量后,引入Maven的配置文件,以便管理項目依賴,最后,編寫代碼實現(xiàn)對HDFS的連接和操作,完成數(shù)據的讀寫,需要的朋友可以參考下
    2022-02-02

最新評論

永善县| 北安市| 榆林市| 黑水县| 墨玉县| 台北市| 彭水| 饶河县| 曲松县| 乌拉特中旗| 栾川县| 卢湾区| 威信县| 玛多县| 宜宾县| 磴口县| 屏边| 宁南县| 瑞丽市| 克山县| 十堰市| 玉田县| 嵊泗县| 上林县| 唐山市| 建德市| 双流县| 延长县| 连山| 金门县| 巴南区| 尼木县| 大方县| 竹山县| 利辛县| 琼海市| 长寿区| 沂南县| 兴仁县| 安徽省| 丰原市|