1. ConcurrentHashMap的實現(xiàn)原理(JDK1.7和JDK1.8)

        共 14404字,需瀏覽 29分鐘

         ·

        2021-04-17 19:14

        點擊上方藍色字體,選擇“標星公眾號”

        優(yōu)質(zhì)文章,第一時間送達

        JDK1.8之前

        HashMap的底層是數(shù)組+鏈表結(jié)合在一起使用。

        1、HashMap 通過 key 的hashCode 經(jīng)過哈希函數(shù)處理過后得到 hash 值;

        2、然后通過 (n - 1) & hash 判斷當(dāng)前元素存放的位置(n 指的是數(shù)組的長度);

        3、如果當(dāng)前位置存在元素的話,就判斷該元素與要存入的元素的 hash值以及 key是否相同;

        4、如果相同的話,直接覆蓋;

        5、不相同就通過拉鏈法解決沖突。

        JDK1.8之后

        HashMap的底層數(shù)據(jù)結(jié)構(gòu)為數(shù)組+鏈表+紅黑樹實現(xiàn)


        當(dāng)鏈表的長度大于閾值(默認為8)時,將鏈表轉(zhuǎn)化為紅黑樹,以減少搜索的時間。


        將鏈表轉(zhuǎn)換成紅黑樹前會判斷,如果當(dāng)前數(shù)組的長度小于 64,那么會選擇先進行數(shù)組擴容,而不是轉(zhuǎn)換為紅黑樹。


        ConcurrentHashMap

        JDK5中添加了新的concurrent包,在線程安全的基礎(chǔ)上提供了更好的寫并發(fā)能力,但同時降低了對讀一致性的要求。


        ConcurrentHashMap是java.util.concurrent下的類;


        在并發(fā)編程中,ConcurrentHashMap是一個經(jīng)常被使用的數(shù)據(jù)結(jié)構(gòu),它的實際與實現(xiàn)非常精巧,大量利用volatile,final,CAS等技術(shù)來減少鎖競爭對于性能的影響。


        簡單的對比

        • HashTable 是一個線程安全的類,它使用synchronized來鎖住整張Hash表來實現(xiàn)線程安全,每次鎖住整張表讓線程獨占,相當(dāng)于所有線程進行讀寫時都去競爭同一把鎖,效率比較低

        • HashMap 不是一個線程安全的類

        • ConcurrentHashMap可以做到讀取數(shù)據(jù)不加鎖,并且其內(nèi)部的結(jié)構(gòu)可以讓其在進行寫操作的時候能夠?qū)㈡i的粒度保持地盡量地小,允許多個修改操作并發(fā)進行,其關(guān)鍵在于使用了鎖分離技術(shù)。它使用了多個鎖來控制對hash表的不同部分進行的修改。只要不爭奪同一把鎖,它們就可以并發(fā)進行。

        JDK1.7


        1、底層的數(shù)據(jù)結(jié)構(gòu)還是數(shù)組+鏈表。鏈表的結(jié)點是HashEntry

        2、采用了segment分段鎖技術(shù),在多線程并發(fā)更新操作時,對同一個segment進行同步加3        鎖,保證數(shù)據(jù)安全。

        3、同步的實現(xiàn)方式使基于ReentrantLock(Segment繼承自ReentrantLock)

        4、存在Hash沖突時,鏈表的查詢效率低

        JDK1.8

        ContcurrentHashMap基于JDK1.8的源碼剖析


        • 底層的數(shù)據(jù)結(jié)構(gòu)與HashMap1.8版本一樣,都是基于數(shù)組+鏈表+紅黑樹

        • 支持多線程的并發(fā)操作,實現(xiàn)的原理是CAS+synchronized保證并發(fā)更新

        • 檢索操作不用加鎖,get方法是非阻塞的

        put方法

        public V put(K key, V value) {
        //實際調(diào)用的是putVal(key,value,false)
        //無論key在表中所對應(yīng)的值是否存在,都使用value進行更新
                return putVal(key, value, false);
            }

            /** Implementation for put and putIfAbsent */
            final V putVal(K key, V value, boolean onlyIfAbsent) {
            //key和value的值必須是非null的
                if (key == null || value == null) throw new NullPointerException();
                //計算key的hash值用來定位元素的位置
                int hash = spread(key.hashCode());
                int binCount = 0;
                //table 引用指向的是ConcurrentHashMap中 所有元素所存在的數(shù)組的引用 所以下面依次將遍歷
                for (ConcurrentHashMap.Node<K,V>[] tab = table;;) {
                    ConcurrentHashMap.Node<K,V> f; int n, i, fh;
                    if (tab == null || (n = tab.length) == 0)
                        tab = initTable();//table為空,則初始化table,首次初始化默認的數(shù)組長度為16
                        
                    else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                   // 判斷key對應(yīng)的數(shù)組位置上是否為null,若尚未發(fā)生hash碰撞,即進行CAS操作,new 一個 Node<K,V>存放到tab中,退出for循環(huán);
                        if (casTabAt(tab, i, null,
                                new ConcurrentHashMap.Node<K,V>(hash, key, value, null)))
                            break;                   // no lock when adding to empty bin
                    }
                    //判斷是否需要擴容
                    else if ((fh = f.hash) == MOVED)   // MOVED = -1
                        tab = helpTransfer(tab, f);
                    else {
                        V oldVal = null;
                        synchronized (f) {//加鎖
                            if (tabAt(tab, i) == f) {
                                if (fh >= 0) {//當(dāng)作鏈表處理
                                    binCount = 1;
                                    for (ConcurrentHashMap.Node<K,V> e = f;; ++binCount) {
                                        K ek;
                                        if (e.hash == hash &&
                                                ((ek = e.key) == key ||
                                                        (ek != null && key.equals(ek)))) {// key 存在,更新
                                            oldVal = e.val;
                                            if (!onlyIfAbsent)
                                                e.val = value;
                                            break;
                                        }
                                        ConcurrentHashMap.Node<K,V> pred = e;
                                        if ((e = e.next) == null) {
                                            pred.next = new ConcurrentHashMap.Node<K,V>(hash, key,
                                                    value, null);//key 不存在,鏈表中追加新元素
                                            break;
                                        }
                                    }
                                }
                                //按照紅黑樹的方式進行插入
                                else if (f instanceof ConcurrentHashMap.TreeBin) {
                                    ConcurrentHashMap.Node<K,V> p;
                                    binCount = 2;
                                    //key不存在則putTreeVal方法直接添加新元素并返回null,key存在則返回對應(yīng)節(jié)點p并做val更新
                                    if ((p = ((ConcurrentHashMap.TreeBin<K,V>)f).putTreeVal(hash, key,
                                            value)) != null) {
                                        oldVal = p.val;
                                        if (!onlyIfAbsent)
                                            p.val = value;
                                    }
                                }
                            }
                        }
                        //當(dāng)插入鏈表后值大于8的時候要轉(zhuǎn)為紅黑樹
                        if (binCount != 0) {
                            if (binCount >= TREEIFY_THRESHOLD)
                                treeifyBin(tab, i);
                            if (oldVal != null)
                                return oldVal;
                            break;
                        }
                    }
                }
                //size++
                addCount(1L, binCount);
                return null;
            }


        put方法流程圖


        1.7和1.8版本都存在的特性

        • ConcurrentHashMap的key和value都不能為null

        • 鍵值迭代器為弱一致性迭代器,創(chuàng)建迭代器后,可以對元素進行更新,對元素更新不會影響遍歷;

        • 讀操作沒有加鎖,value是voliate修飾的,保證了可見性

        • 讀寫分離提高效率:多線程對不同的Node/Segment 的插入和刪除是可以并發(fā)、并行執(zhí)行的,對同一個Node/Segment的寫操作是互斥的。讀操作都是無鎖的操作,可以并發(fā)、并行執(zhí)行。

        HashMap,HashTable和ConcurrentHashMap的區(qū)別:

        ————————————————

        版權(quán)聲明:本文為CSDN博主「小王~同學(xué)」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。

        原文鏈接:

        https://blog.csdn.net/qq_45754055/article/details/115592109





        粉絲福利:Java從入門到入土學(xué)習(xí)路線圖

        ??????

        ??長按上方微信二維碼 2 秒


        感謝點贊支持下哈 

        瀏覽 55
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 黄色小说看| 亚洲午夜福利在线观看 | chineseav在线 | 免费无遮挡网站 | 一边摸一边爽一边叫床视频 |