ConCurrentHashMap的實現原理

ConCurrentHashMap的實現原理

    一、JDK1.6和JDK1.7中的實現

  1. 設計思路
    ConCurrentHashMap採用分段鎖的思想,每一個段都有一把鎖,從而提高了併發度(即同時操作ConCurrentHashMap而不產生鎖競爭的線程最大數)。另外,當設置併發數時,會默認將併發數改爲2的冪。
    所謂的段,就是segment,他和HashMap的結構很像,都在內部有一個Entry數組,並且繼承了重入鎖,用於對段的上鎖。
  2. 分段創建
    JDK1.6會在一開始就創建所有的segment,而JDK1.7除了第一個segment,其餘的segment都是在put時,動態創建的,所以,在每一次查找段的時候,都會判斷段是否爲空,如果爲空則創建。由於ensureSegment方法要在併發場景被調用,所以創建段時,通過CAS算法實現,代碼如下:
    if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
                    == null) { // recheck
                    Segment<K,V> s = new Segment<K,V>(lf, threshold, tab);
                    while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
                           == null) {
                        if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s))
                            break;
                    }
    }
  3. put方法
    與JDK1.6不同的是,JDK1.7在put時,會用tryLock(),其餘並無不同。
    put時,先通過key找到對應的段,在找到段內的entry,段內操作和HashMap類似。
  4. get方法和containsKey方法
    由於ConCurrentHashMap的get方法和containsKey都是不加鎖的,所以很有可能當ConCurrentHashMap遍歷過程中有其他的線程對其內容進行更改,導致兩方法返回的數據不是最新的數據。
  5. size方法
    size方法也會使用CAS對modcount進行判斷,當多次重試後,會對所有的segment上鎖。
  6. ConCurrentHashMap不允許key和value爲空

    二、JDK1.8的實現

  1. 設計思路
    JDK1.8取消了segment的概念,還是使用的HashMap中數組加鏈表的數據結構,但是對於每一個數組進行加鎖,以調整鎖的粒度。同時,爲了防止hash衝突造成的鏈表過長,當鏈表長度大於8時,會把鏈表變爲紅黑二叉樹的數據結構,當紅黑樹的長度小於等於6時,又可以轉化爲鏈表。

  2. 使用了synchronized關鍵字而不是重入鎖
    可見JDK1.8對synchronized進行了性能優化。
發佈了73 篇原創文章 · 獲贊 13 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章