亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java中的concurrenthashmap集合詳細(xì)剖析

 更新時(shí)間:2023年11月17日 08:48:25   作者:top啦它  
這篇文章主要介紹了Java中的concurrenthashmap集合詳細(xì)剖析,有參構(gòu)造后第一次put時(shí)會(huì)進(jìn)行初始化,由源碼可知,會(huì)先判斷所傳入的容量是否>=最大容量的一半,如果滿足條件,就會(huì)將容量修改為最大值,反之則會(huì)將容量改為所傳入數(shù)*1.5+1,需要的朋友可以參考下

concurrenthashmap

有參構(gòu)造后第一次put時(shí)會(huì)進(jìn)行初始化。初始化的容量并不是所傳入的數(shù)。

源碼:

    public ConcurrentHashMap(int initialCapacity) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException();
        int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
                   MAXIMUM_CAPACITY :
                   tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
        this.sizeCtl = cap;
    }

由源碼可知,會(huì)先判斷所傳入的容量是否>=最大容量的一半,如果滿足條件,就會(huì)將容量修改為最大值,反之則會(huì)將容量改為所傳入數(shù)*1.5+1。

并且這個(gè)tableSizeFor函數(shù)會(huì)將這個(gè)數(shù)修改為2**n>initialCapacity + (initialCapacity >>> 1) + 1的最小2**n。

舉個(gè)例子證明一下:

        ConcurrentHashMap<String,String> a = new ConcurrentHashMap<>(32);
        Class<? extends ConcurrentHashMap> aClass = a.getClass();
        Field sizeCtl = aClass.getDeclaredField("sizeCtl");
        sizeCtl.setAccessible(true);
        System.out.println(sizeCtl.getName()+":"+sizeCtl.get(a));

輸出如下:

sizeCtl:64

這里的sizeCtl有四種情況。

類型含義
sizeCtl為0代表此時(shí)還未進(jìn)行初始化操作。
sizeCtl為正數(shù)若還未初始化則代表的是初始容量,已經(jīng)初始化則代表的是擴(kuò)容閾值
sizeCtl為-1代表正在進(jìn)行初始化操作
sizeCtl為負(fù)數(shù)不為-1時(shí)代表此時(shí)有-1*(1+n)個(gè)線程正在進(jìn)行擴(kuò)容操作

當(dāng)?shù)谝淮螌懭霐?shù)據(jù)時(shí):

源碼如下:

public V put(K key, V value) {
        return putVal(key, value, false);
    }

    /** Implementation for put and putIfAbsent */
    final V putVal(K key, V value, boolean onlyIfAbsent) {//onlyIfAbsent是當(dāng)存在相同元素時(shí)是否覆蓋。這里傳入了false就是覆蓋的意思。
        if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode());//計(jì)算key對象的hash值
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {//這里是一個(gè)死循環(huán)。
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)//如果table還未進(jìn)行初始化操作,在第一次寫入數(shù)據(jù)時(shí)會(huì)進(jìn)行初始化。
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }
for (Node<K,V>[] tab = table;;) {//這里是一個(gè)死循環(huán)。這個(gè)循環(huán)中存在4個(gè)判斷
分別是:
1、if (tab == null || (n = tab.length) == 0)//代表還未初始化或者當(dāng)前長度為0。
2、else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//根據(jù)key的hash值來判斷將要放入哪個(gè)位置,并將f指向這個(gè)位置。
如果這個(gè)位置目前還不存在數(shù)據(jù)的話。就會(huì)進(jìn)入
if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;  語句。這里存在一個(gè)cas自旋。并且會(huì)進(jìn)行雙重檢測,一旦發(fā)現(xiàn)符合條件,就會(huì)將要添加的節(jié)點(diǎn)放入f指向的位置,并跳出循環(huán),結(jié)束。
3、else if ((fh = f.hash) == MOVED)//MOVED為-1,表示當(dāng)前entry已經(jīng)遷移,里面的tab = helpTransfer(tab, f);表示協(xié)助遷移。遷移完成后或再次循環(huán)并進(jìn)行判斷。
4、else中有一把瑣synchronized (f) 。在上面的代碼中i已經(jīng)被賦值為(n - 1) & hash)。
這把鎖所包括的代碼塊中
存在雙重檢測代碼if (tabAt(tab, i) == f)。
他的存在就是為了檢測曾經(jīng)獲得的桶的頭節(jié)點(diǎn)的位置是否已經(jīng)修改。
因?yàn)樵趍ap中存在樹-->鏈表和鏈表->樹的操作,這回改變桶頭節(jié)點(diǎn)的位置。
但是這并不能保證安全,
因?yàn)樵谵D(zhuǎn)化后頭節(jié)點(diǎn)的位置可能不會(huì)發(fā)生改變,所以還存在if (fh >= 0)這一句代碼,
表示f這個(gè)節(jié)點(diǎn)的hash。
樹節(jié)點(diǎn)的hash時(shí)=是負(fù)數(shù)。
ok,如果未修改的話他會(huì)遍歷桶下面的節(jié)點(diǎn),判斷是否存在相同的節(jié)點(diǎn)。如果存在的話,
就覆蓋,不存在就加到鏈表尾部。
在添加完成后執(zhí)行:
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
 這部分代碼表示是否進(jìn)行樹化。如果當(dāng)前map的長度不小于64,那么當(dāng)一個(gè)桶中的元素大于等于8的時(shí)候,會(huì)進(jìn)行
 樹化。
 最后執(zhí)行addCount(1L, binCount);
 這個(gè)代碼很復(fù)雜??梢詤⒖糷ttps://www.bilibili.com/video/BV17i4y1x71z?from=search&seid=2906009396999368521&spm_id_from=333.337.0.0
 

初始化源碼:

    private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
        while ((tab = table) == null || tab.length == 0) {
            if ((sc = sizeCtl) < 0)
                Thread.yield(); // lost initialization race; just spin
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    if ((tab = table) == null || tab.length == 0) {
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        @SuppressWarnings("unchecked")
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                        table = tab = nt;
                        sc = n - (n >>> 2);
                    }
                } finally {
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }

我的理解是在嗎map初始化之后,map的長度縮減到0的話,會(huì)重新進(jìn)行一次初始化。

在并發(fā)環(huán)境下初始化的時(shí)候有可能會(huì)有多個(gè)線程進(jìn)入此方法。

不過這里存在一個(gè)cas自旋鎖U.compareAndSwapInt(this, SIZECTL, sc, -1),首先,當(dāng)未初始化的時(shí)候sizeCtl為0,所以會(huì)進(jìn)入else if??赡軙?huì)有多個(gè)線程符合標(biāo)準(zhǔn)進(jìn)入else if,但是這里存在一個(gè)cas自旋鎖,當(dāng)?shù)谝粋€(gè)觸碰到cas的線程進(jìn)行操作后,sizeCtl就會(huì)被修改為-1,如果修改成功則返回true,當(dāng)另一個(gè)線程進(jìn)入自旋鎖后會(huì)發(fā)現(xiàn)sizeCtl, sc并不相等了,所以會(huì)返回false,離開else if,這時(shí)再吃循環(huán)到if語句時(shí)會(huì)發(fā)現(xiàn)sizeCtl=-1,然后進(jìn)入if,執(zhí)行Thread.yield(),謙讓出cpu。

直到初始化完成sizeCtl為擴(kuò)容閾值為止。此時(shí)sizeCtl已經(jīng)不滿足if語句了,所以那些還在謙讓的線程就會(huì)開始陸陸續(xù)續(xù)的執(zhí)行else if語句,發(fā)現(xiàn)cas返回true,然后再else if中存在雙重檢測,判斷table是否為null。很顯然并不滿足。

于是執(zhí)行finally中的語句,最后跳出循環(huán),返回table。

到此這篇關(guān)于Java中的concurrenthashmap集合詳細(xì)剖析的文章就介紹到這了,更多相關(guān)concurrenthashmap集合內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java線程的調(diào)度與優(yōu)先級(jí)詳解

    Java線程的調(diào)度與優(yōu)先級(jí)詳解

    這篇文章主要為大家詳細(xì)介紹了Java線程的調(diào)度與優(yōu)先級(jí),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • Java實(shí)踐練習(xí)輕松幾行實(shí)現(xiàn)追書神器

    Java實(shí)踐練習(xí)輕松幾行實(shí)現(xiàn)追書神器

    讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Java實(shí)現(xiàn)一個(gè)追書神器,用技術(shù)改變生活,大家可以在過程中查缺補(bǔ)漏,提升水平
    2021-10-10
  • Spring Data JPA中的Specification動(dòng)態(tài)查詢詳解

    Spring Data JPA中的Specification動(dòng)態(tài)查詢詳解

    Specification是一個(gè)設(shè)計(jì)模式,用于企業(yè)級(jí)應(yīng)用開發(fā)中,其主要目的是將業(yè)務(wù)規(guī)則從業(yè)務(wù)邏輯中分離出來,在數(shù)據(jù)查詢方面,Specification可以定義復(fù)雜的查詢,使其更易于重用和測試,這篇文章主要介紹了Spring Data JPA中的Specification動(dòng)態(tài)查詢詳解,需要的朋友可以參考下
    2023-07-07
  • JVM原理之類加載的全過程

    JVM原理之類加載的全過程

    文章詳細(xì)介紹了Java類加載過程,包括加載、鏈接、初始化、使用和卸載五個(gè)階段,并解釋了符號(hào)引用和直接引用的區(qū)別,以及類變量和實(shí)例變量的區(qū)別,此外,還介紹了Class.forName()方法的作用和使用場景
    2025-01-01
  • Java集合操作之List接口及其實(shí)現(xiàn)方法詳解

    Java集合操作之List接口及其實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了Java集合操作之List接口及其實(shí)現(xiàn)方法,詳細(xì)分析了Java集合操作中List接口原理、功能、用法及操作注意事項(xiàng),需要的朋友可以參考下
    2015-07-07
  • Java語言十大基礎(chǔ)特性分析

    Java語言十大基礎(chǔ)特性分析

    這篇文章介紹了Java語言十大基礎(chǔ)特性,它有哪些優(yōu)勢,需要的朋友可以參考下。
    2017-08-08
  • Spring?Cloud?中使用?Sentinel?實(shí)現(xiàn)服務(wù)限流的兩種方式

    Spring?Cloud?中使用?Sentinel?實(shí)現(xiàn)服務(wù)限流的兩種方式

    這篇文章主要介紹了Spring?Cloud?中使用?Sentinel?實(shí)現(xiàn)服務(wù)限流的方式,通過示例代碼主要介紹了Sentinel的兩種實(shí)現(xiàn)限流的方式,需要的朋友可以參考下
    2024-03-03
  • 詳解SpringBoot可執(zhí)行Jar包運(yùn)行原理

    詳解SpringBoot可執(zhí)行Jar包運(yùn)行原理

    SpringBoot有一個(gè)很方便的功能就是可以將應(yīng)用打成可執(zhí)行的Jar,那么大家有沒想過這個(gè)Jar是怎么運(yùn)行起來的呢,本篇博客就來介紹下 SpringBoot可執(zhí)行Jar包的運(yùn)行原理,需要的朋友可以參考下
    2023-05-05
  • Java算法之遞歸算法計(jì)算階乘

    Java算法之遞歸算法計(jì)算階乘

    這篇文章主要為大家詳細(xì)介紹了Java遞歸算法計(jì)算階乘,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2015-08-08
  • Kafka Java Producer代碼實(shí)例詳解

    Kafka Java Producer代碼實(shí)例詳解

    這篇文章主要介紹了Kafka Java Producer代碼實(shí)例詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06

最新評(píng)論