成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JDK1.8下ConcurrentHashMap的一些理解(一)

Andrman / 388人閱讀

摘要:如下代碼省略相關(guān)代碼省略相關(guān)代碼可以看到在里面,是直接采用數(shù)組鏈表紅黑樹來實現(xiàn),時間復(fù)雜度在和之間,如果鏈表轉(zhuǎn)化為紅黑樹了,那么就是到。

在JDK1.8里面,ConcurrentHashMap在put方法里面已經(jīng)將分段鎖移除了,轉(zhuǎn)而是CAS鎖和synchronized

ConcurrentHashMap是Java里面同時兼顧性能和線程安全的一個鍵值對集合,同屬于鍵值對的集合還有HashTable以及HashMap
HashTable是一個線程安全的類,因為它的所有public方法都被synchronized修飾,這樣就導(dǎo)致了一個問題,就是效率太低。

雖然HashMapJDK1.8的并發(fā)場景下觸發(fā)擴(kuò)容時不會出現(xiàn)成環(huán)了,但是會出現(xiàn)數(shù)據(jù)丟失的情況。
所以如果需要在多線程的情況下(多讀少寫))使用Map集合的話,ConcurrentHashMap是一個不錯的選擇。

ConcurrentHashMap在JDK1.8的時候?qū)ut()方法中的分段鎖Segment移除,轉(zhuǎn)而采用一種CAS鎖和synchronized來實現(xiàn)插入方法的線程安全。
如下代碼:

/** Implementation for put and putIfAbsent */
    final V putVal(K key, V value, boolean onlyIfAbsent) {
       //省略相關(guān)代碼
        for (Node[] tab = table;;) {
            Node f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null,
                             new Node(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) {
                    //省略相關(guān)代碼
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

可以看到在JDK1.8里面,ConcurrentHashMap是直接采用數(shù)組+鏈表+紅黑樹來實現(xiàn),時間復(fù)雜度在O(1)和O(n)之間,如果鏈表轉(zhuǎn)化為紅黑樹了,那么就是O(1)到O(nlogn)。
在這里值得一提的是,ConcurrentHashMap會判斷tabAt(tab, i = (n - 1) & hash)是不是 null,是的話就直接采用CAS進(jìn)行插入,而如果不為空的話,則是synchronized鎖住當(dāng)前Node的首節(jié)點(diǎn),這是因為當(dāng)該Node不為空的時候,證明了此時出現(xiàn)了Hash碰撞,就會涉及到鏈表的尾節(jié)點(diǎn)新增或者紅黑樹的節(jié)點(diǎn)新增以及紅黑樹的平衡,這些操作自然都是非原子性的。

從而導(dǎo)致無法使用CAS,當(dāng)Node的當(dāng)前下標(biāo)為null的時候,由于只是涉及數(shù)組的新增,所以用CAS即可。

因為CAS是一種基于版本控制的方式來實現(xiàn),而碰撞之后的操作太多,所以直接用synchronized比較合適。
ConcurrentHashMap在迭代時和HashMap的區(qū)別

當(dāng)一個集合在迭代的時候如果動態(tài)的添加或者刪除元素,那么就會拋出Concurrentmodificationexception,但是在ConcurrentHashMap里面卻不會,例如如下代碼:

public static void main(String[] args) {
    Map map = new ConcurrentHashMap();
    map.put("a","a1");
    map.put("b","b1");
    map.put("c","c1");
    map.put("d","d1");
    map.put("e","e1");
    Iterator iterator = map.keySet().iterator();
    while (iterator.hasNext()){
        String it = iterator.next();
        if("b".equals(it)){
            map.remove("d");
        }
        System.out.println(it);
    }
}

控制臺打印如下:
a
b
c
e

而當(dāng)你把ConcurrentHashMap換成HashMap的時候,控制臺就會拋出一個異常:

Exception in thread "main" a
b
java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
    at java.util.HashMap$KeyIterator.next(HashMap.java:1466)
    at xyz.somersames.ListTest.main(ListTest.java:22)

原因在于ConcurrentHashMapnext方法并不會去檢查modCountexpectedModCount,但是會檢查下一個節(jié)點(diǎn)是不是為空

  if ((p = next) == null)
    throw new NoSuchElementException();

當(dāng)我們進(jìn)行remove的時候,ConcurrentHashMap會直接通過修改指針的方式來進(jìn)行移除操作,同樣的,也會鎖住數(shù)組的頭節(jié)點(diǎn)直至移除結(jié)束,所以在同一個時刻,只會有一個線程對當(dāng)前數(shù)組下標(biāo)的所有節(jié)點(diǎn)進(jìn)行操作。

但是在HashMap里面,next方法會進(jìn)行一個check,而remove操作會修改modCount,導(dǎo)致modCountexpectedModCount不相等,所以就會導(dǎo)致
ConcurrentModificationException

稍微修改下代碼:

public static void main(String[] args) {
    Map map = new ConcurrentHashMap();
    map.put("a","a1");
    map.put("b","b1");
    map.put("c","c1");
    map.put("d","d1");
    map.put("e","e1");
    Iterator iterator = map.keySet().iterator();
    while (iterator.hasNext()){
        if("b".equals(iterator.next())){
            map.remove("d");
        }
        System.out.println(iterator.next());
    }
}
控制臺打印如下:
b
d
Exception in thread "main" java.util.NoSuchElementException
    at java.util.concurrent.ConcurrentHashMap$KeyIterator.next(ConcurrentHashMap.java:3416)
    at com.xzh.ssmtest.ListTest.main(ListTest.java:25)
并發(fā)下的處理

由于每一個Node的首節(jié)點(diǎn)都會被synchronized修飾,從而將一個元素的新增轉(zhuǎn)化為一個原子操作,同時Nodevaluenext都是由volatile關(guān)鍵字進(jìn)行修飾,從而可以保證可見性。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/74650.html

相關(guān)文章

  • ConcurrentHashMap基于JDK1.8源碼剖析

    摘要:下面我來簡單總結(jié)一下的核心要點(diǎn)底層結(jié)構(gòu)是散列表數(shù)組鏈表紅黑樹,這一點(diǎn)和是一樣的。是將所有的方法進(jìn)行同步,效率低下。而作為一個高并發(fā)的容器,它是通過部分鎖定算法來進(jìn)行實現(xiàn)線程安全的。 前言 聲明,本文用的是jdk1.8 前面章節(jié)回顧: Collection總覽 List集合就這么簡單【源碼剖析】 Map集合、散列表、紅黑樹介紹 HashMap就是這么簡單【源碼剖析】 LinkedHas...

    sanyang 評論0 收藏0
  • 趣談ConcurrentHashMap

    摘要:最近準(zhǔn)備面試,一談到基礎(chǔ),大部分面試官上來就數(shù)據(jù)結(jié)構(gòu)素質(zhì)三連與區(qū)別,底層數(shù)據(jù)結(jié)構(gòu),為什么能保證線程安全。數(shù)組順序存儲,內(nèi)存連續(xù),查詢快,插入刪除效率稍微低,不過現(xiàn)在略有改善。而在開始,是由和的方式去實現(xiàn)高并發(fā)下的線程安全。 最近準(zhǔn)備面試,一談到j(luò)ava基礎(chǔ),大部分面試官上來就java數(shù)據(jù)結(jié)構(gòu)素質(zhì)三連:ArrayList與LinkedList區(qū)別,HashMap底層數(shù)據(jù)結(jié)構(gòu),Concur...

    Travis 評論0 收藏0
  • 這幾道Java集合框架面試題在面試中幾乎必問

    摘要:若遇到哈希沖突,則將沖突的值加到鏈表中即可。之后相比于之前的版本,之后在解決哈希沖突時有了較大的變化,當(dāng)鏈表長度大于閾值默認(rèn)為時,將鏈表轉(zhuǎn)化為紅黑樹,以減少搜索時間。有序,唯一紅黑樹自平衡的排序二叉樹。 本文是最最最常見Java面試題總結(jié)系列第三周的文章。主要內(nèi)容: Arraylist 與 LinkedList 異同 ArrayList 與 Vector 區(qū)別 HashMap的底層...

    bigdevil_s 評論0 收藏0
  • ConcurrentHashMap源碼分析_JDK1.8版本

    ConcurrentHashMap源碼分析_JDK1.8版本 聲明 文章均為本人技術(shù)筆記,轉(zhuǎn)載請注明出處[1] https://segmentfault.com/u/yzwall[2] blog.csdn.net/j_dark/ JDK1.6版本 ConcurrentHashMap結(jié)構(gòu) showImg(https://segmentfault.com/img/remote/146000000900...

    animabear 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<