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

ConcurrentHashMap是如何保證線程安全

 更新時間:2022年01月24日 09:50:55   作者:磊哥聊編程  
大家好,本篇文章主要講的是ConcurrentHashMap是如何保證線程安全,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下

ConcurrentHashMap 是 HashMap 的多線程版本,HashMap 在并發(fā)操作時會有各種問題,比如死循環(huán)問題、數(shù)據(jù)覆蓋等問題。而這些問題,只要使用 ConcurrentHashMap 就可以完美解決了,那問題來了,ConcurrentHashMap 是如何保證線程安全的?它的底層又是如何實現(xiàn)的?接下來我們一起來看。

JDK 1.7 底層實現(xiàn)

ConcurrentHashMap 在不同的 JDK 版本中實現(xiàn)是不同的,在 JDK 1.7 中它使用的是數(shù)組加鏈表的形式實現(xiàn)的,而數(shù)組又分為:大數(shù)組 Segment 和小數(shù)組 HashEntry。 大數(shù)組 Segment 可以理解為 MySQL 中的數(shù)據(jù)庫,而每個數(shù)據(jù)庫(Segment)中又有很多張表 HashEntry,每個 HashEntry 中又有多條數(shù)據(jù),這些數(shù)據(jù)是用鏈表連接的,如下圖所示:

為什么ConcurrentHashMap是線程安全的?_數(shù)組

JDK 1.7 線程安全實現(xiàn)

了解了 ConcurrentHashMap 的底層實現(xiàn),再看它的線程安全實現(xiàn)就比較簡單了。
接下來,我們通過添加元素 put 方法,來看 JDK 1.7 中 ConcurrentHashMap 是如何保證線程安全的,具體實現(xiàn)源碼如下:

final V put(K key, int hash, V value, boolean onlyIfAbsent) {
    // 在往該 Segment 寫入前,先確保獲取到鎖
    HashEntry<K,V> node = tryLock() ? null : scanAndLockForPut(key, hash, value); 
    V oldValue;
    try {
        // Segment 內(nèi)部數(shù)組
        HashEntry<K,V>[] tab = table;
        int index = (tab.length - 1) & hash;
        HashEntry<K,V> first = entryAt(tab, index);
        for (HashEntry<K,V> e = first;;) {
            if (e != null) {
                K k;
                // 更新已有值...
            }
            else {
                // 放置 HashEntry 到特定位置,如果超過閾值則進(jìn)行 rehash
                // 忽略其他代碼...
            }
        }
    } finally {
        // 釋放鎖
        unlock();
    }
    return oldValue;
}

從上述源碼我們可以看出,Segment 本身是基于 ReentrantLock 實現(xiàn)的加鎖和釋放鎖的操作,這樣就能保證多個線程同時訪問 ConcurrentHashMap 時,同一時間只有一個線程能操作相應(yīng)的節(jié)點,這樣就保證了 ConcurrentHashMap 的線程安全了。
也就是說 ConcurrentHashMap 的線程安全是建立在 Segment 加鎖的基礎(chǔ)上的,所以我們把它稱之為分段鎖或片段鎖,如下圖所示:

為什么ConcurrentHashMap是線程安全的?_數(shù)組_02

JDK 1.8 底層實現(xiàn)

在 JDK 1.7 中,ConcurrentHashMap 雖然是線程安全的,但因為它的底層實現(xiàn)是數(shù)組

DK 1.7 中,ConcurrentHashMap 雖然是線程安全的,但因為它的底層實現(xiàn)是數(shù)組 + 鏈表的形式,所以在數(shù)據(jù)比較多的情況下訪問是很慢的,因為要遍歷整個鏈表,而 JDK 1.8 則使用了數(shù)組 + 鏈表/紅黑樹的方式優(yōu)化了 ConcurrentHashMap 的實現(xiàn),具體實現(xiàn)結(jié)構(gòu)如下:

為什么ConcurrentHashMap是線程安全的?_鏈表_03

鏈表升級為紅黑樹的規(guī)則:當(dāng)鏈表長度大于 8,并且數(shù)組的長度大于 64 時,鏈表就會升級為紅黑樹的結(jié)構(gòu)。

PS:ConcurrentHashMap 在 JDK 1.8 雖然保留了 Segment 的定義,但這僅僅是為了保證序列化時的兼容性,不再有任何結(jié)構(gòu)上的用處了。

JDK 1.8 線程安全實現(xiàn)

在 JDK 1.8 中 ConcurrentHashMap 使用的是 CAS

DK 1.8 中 ConcurrentHashMap 使用的是 CAS + volatile 或 synchronized 的方式來保證線程安全的,它的核心實現(xiàn)源碼如下:

final V putVal(K key, V value, boolean onlyIfAbsent) { if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode());
    int binCount = 0;
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh; K fk; V fv;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // 節(jié)點為空
            // 利用 CAS 去進(jìn)行無鎖線程安全操作,如果 bin 是空的
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
                break; 
        }
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else if (onlyIfAbsent
                 && fh == hash
                 && ((fk = f.key) == key || (fk != null && key.equals(fk)))
                 && (fv = f.val) != null)
            return fv;
        else {
            V oldVal = null;
            synchronized (f) {
                   // 細(xì)粒度的同步修改操作... 
                }
            }
            // 如果超過閾值,升級為紅黑樹
            if (binCount != 0) {
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount);
    return null;
}

從上述源碼可以看出,在 JDK 1.8 中,添加元素時首先會判斷容器是否為空,如果為空則使用 volatile 加 CAS 來初始化。如果容器不為空則根據(jù)存儲的元素計算該位置是否為空,如果為空則利用 CAS 設(shè)置該節(jié)點;如果不為空則使用 synchronize 加鎖,遍歷桶中的數(shù)據(jù),替換或新增節(jié)點到桶中,最后再判斷是否需要轉(zhuǎn)為紅黑樹,這樣就能保證并發(fā)訪問時的線程安全了。
我們把上述流程簡化一下,我們可以簡單的認(rèn)為在 JDK 1.8 中,ConcurrentHashMap 是在頭節(jié)點加鎖來保證線程安全的,鎖的粒度相比 Segment 來說更小了,發(fā)生沖突和加鎖的頻率降低了,并發(fā)操作的性能就提高了。而且 JDK 1.8 使用的是紅黑樹優(yōu)化了之前的固定鏈表,那么當(dāng)數(shù)據(jù)量比較大的時候,查詢性能也得到了很大的提升,從之前的 O(n) 優(yōu)化到了 O(logn) 的時間復(fù)雜度,具體加鎖示意圖如下:

為什么ConcurrentHashMap是線程安全的?_線程安全_04

總結(jié)

ConcurrentHashMap 在 JDK 1.7 時使用的是數(shù)據(jù)加鏈表的形式實現(xiàn)的,其中數(shù)組分為兩類:大數(shù)組 Segment 和小數(shù)組 HashEntry,而加鎖是通過給 Segment 添加 ReentrantLock 鎖來實現(xiàn)線程安全的。而 JDK 1.8 中 ConcurrentHashMap 使用的是數(shù)組+鏈表/紅黑樹的方式實現(xiàn)的,它是通過 CAS 或 synchronized 來實現(xiàn)線程安全的,并且它的鎖粒度更小,查詢性能也更高。

到此這篇關(guān)于ConcurrentHashMap是如何保證線程安全的文章就介紹到這了,更多相關(guān)ConcurrentHashMap線程安全內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Lombok中關(guān)于@Data的使用解析

    Lombok中關(guān)于@Data的使用解析

    這篇文章主要介紹了Lombok中關(guān)于@Data的使用解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java中Elasticsearch的核心概念詳解

    Java中Elasticsearch的核心概念詳解

    這篇文章主要介紹了Java中Elasticsearch的核心概念詳解,Elasticsearch 是一個分布式、免費和開放的搜索和分析引擎,適用于所有類型的數(shù)據(jù),包括文本、數(shù)字、地理空間、結(jié)構(gòu)化和非結(jié)構(gòu)化數(shù)據(jù),需要的朋友可以參考下
    2023-07-07
  • Java 給PDF簽名時添加可信時間戳的方法

    Java 給PDF簽名時添加可信時間戳的方法

    這篇文章主要介紹了Java 給PDF簽名時添加可信時間戳,關(guān)于jar導(dǎo)入的問題,本文給大家?guī)韮煞N方法,一種是手動導(dǎo)入另一種是maven配置導(dǎo)入,需要的朋友可以參考下
    2021-07-07
  • JDBC數(shù)據(jù)庫連接過程及驅(qū)動加載與設(shè)計模式詳解

    JDBC數(shù)據(jù)庫連接過程及驅(qū)動加載與設(shè)計模式詳解

    這篇文章主要介紹了JDBC數(shù)據(jù)庫連接過程及驅(qū)動加載與設(shè)計模式詳解,需要的朋友可以參考下
    2016-10-10
  • Java 8新的時間日期庫的20個使用示例

    Java 8新的時間日期庫的20個使用示例

    這篇文章主要介紹了Java 8新的時間日期庫的20個使用示例,需要的朋友可以參考下
    2015-04-04
  • IDEA中關(guān)于enter鍵換行的問題

    IDEA中關(guān)于enter鍵換行的問題

    這篇文章主要介紹了IDEA中關(guān)于enter鍵換行的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • java之Thread不捕獲異常默認(rèn)處理邏輯

    java之Thread不捕獲異常默認(rèn)處理邏輯

    這篇文章主要介紹了java之Thread不捕獲異常默認(rèn)處理邏輯,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Java Web實現(xiàn)session過期后自動跳轉(zhuǎn)到登陸頁功能【基于過濾器】

    Java Web實現(xiàn)session過期后自動跳轉(zhuǎn)到登陸頁功能【基于過濾器】

    這篇文章主要介紹了Java Web實現(xiàn)session過期后自動跳轉(zhuǎn)到登陸頁功能,涉及java過濾器針對session的判斷與跳轉(zhuǎn)相關(guān)操作技巧,需要的朋友可以參考下
    2017-11-11
  • MyBatis-Plus中的邏輯刪除使用詳解

    MyBatis-Plus中的邏輯刪除使用詳解

    開發(fā)系統(tǒng)時,有時候在實現(xiàn)功能時,刪除操作需要實現(xiàn)邏輯刪除就是將數(shù)據(jù)標(biāo)記為刪除,而并非真的物理刪除(非DELETE操作),查詢時需要攜帶狀態(tài)條件,確保被標(biāo)記的數(shù)據(jù)不被查詢到。這樣做的目的就是避免數(shù)據(jù)被真正的刪除
    2022-12-12
  • 基于Scala和Java方法的相互調(diào)用

    基于Scala和Java方法的相互調(diào)用

    這篇文章主要介紹了Scala和Java方法的相互調(diào)用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11

最新評論

永嘉县| 尖扎县| 宜黄县| 高邑县| 柳江县| 温泉县| 丘北县| 固阳县| 贵德县| 勃利县| 泰来县| 阿拉尔市| 和政县| 太仆寺旗| 海安县| 新乡县| 乌鲁木齐县| 磴口县| 腾冲县| 嫩江县| 肥东县| 北安市| 桓台县| 麦盖提县| 金坛市| 稷山县| 尉犁县| 廉江市| 信宜市| 广丰县| 峨山| 育儿| 靖远县| 叙永县| 舟山市| 茶陵县| 南开区| 茂名市| 抚州市| 左贡县| 岳普湖县|