ConCurrentHashMap在1.7和1.8區別


ConCurrentHashMap 1.8 相比 1.7的話,主要改變為:

  • 去除 Segment + HashEntry + Unsafe 的實現,
    改為 Synchronized + CAS + Node + Unsafe 的實現
    其實 Node 和 HashEntry 的內容一樣,但是HashEntry是一個內部類。
    用 Synchronized + CAS 代替 Segment ,這樣鎖的粒度更小了,並且不是每次都要加鎖了,CAS嘗試失敗了在加鎖。

  • put()方法中 初始化數組大小時,1.8不用加鎖,因為用了個 sizeCtl 變量,將這個變量置為-1,就表明table正在初始化。

下面簡單介紹下主要的幾個方法的一些區別:

1. put() 方法

JDK1.7中的實現:

ConCurrentHashMap 和 HashMap 的put()方法實現基本類似,所以主要講一下為了實現並發性,ConCurrentHashMap 1.7 有了什么改變

  • 需要定位 2 次 (segments[i],segment中的table[i])
    由於引入segment的概念,所以需要

    1. 先通過key的 rehash值的高位segments數組大小-1 相與得到在 segments中的位置
    2. 然后在通過 key的rehash值table數組大小-1 相與得到在table中的位置
  • 沒獲取到 segment鎖的線程,沒有權力進行put操作,不是像HashTable一樣去掛起等待,而是會去做一下put操作前的准備:

    1. table[i]的位置(你的值要put到哪個桶中)
    2. 通過首節點first遍歷鏈表找有沒有相同key
    3. 在進行1、2的期間還不斷自旋獲取鎖,超過 64次 線程掛起!

JDK1.8中的實現:

  • 先拿到根據 rehash值 定位,拿到table[i]的 首節點first,然后:
    1. 如果為 null ,通過 CAS 的方式把 value put進去
    2. 如果 非null ,並且 first.hash == -1 ,說明其他線程在擴容,參與一起擴容
    3. 如果 非null ,並且 first.hash != -1 ,Synchronized鎖住 first節點,判斷是鏈表還是紅黑樹,遍歷插入。

2. get() 方法

JDK1.7中的實現:

  • 由於變量 value 是由 volatile 修飾的,java內存模型中的 happen before 規則保證了 對於 volatile 修飾的變量始終是 寫操作 先於 讀操作 的,並且還有 volatile 的 內存可見性 保證修改完的數據可以馬上更新到主存中,所以能保證在並發情況下,讀出來的數據是最新的數據。

  • 如果get()到的是null值才去加鎖。

JDK1.8中的實現:

  • 和 JDK1.7類似

3. resize() 方法

JDK1.7中的實現:

  • 跟HashMap的 resize() 沒太大區別,都是在 put() 元素時去做的擴容,所以在1.7中的實現是獲得了鎖之后,在單線程中去做擴容(1.new個2倍數組 2.遍歷old數組節點搬去新數組)。

JDK1.8中的實現:

  • jdk1.8的擴容支持並發遷移節點,從old數組的尾部開始,如果該桶被其他線程處理過了,就創建一個 ForwardingNode 放到該桶的首節點,hash值為-1,其他線程判斷hash值為-1后就知道該桶被處理過了。

4. 計算size

JDK1.7中的實現:

    1. 先采用不加鎖的方式,計算兩次,如果兩次結果一樣,說明是正確的,返回。
    1. 如果兩次結果不一樣,則把所有 segment 鎖住,重新計算所有 segment的 Count 的和

JDK1.8中的實現:

由於沒有segment的概念,所以只需要用一個 baseCount 變量來記錄ConcurrentHashMap 當前 節點的個數

    1. 先嘗試通過CAS 修改 baseCount
    1. 如果多線程競爭激烈,某些線程CAS失敗,那就CAS嘗試將 CELLSBUSY 置1,成功則可以把 baseCount變化的次數 暫存到一個數組 counterCells 里,后續數組 counterCells 的值會加到 baseCount 中。
    1. 如果 CELLSBUSY 置1失敗又會反復進行CASbaseCount 和 CAScounterCells數組





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM