日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ConcurrentHashMap 原理解析

發布時間:2024/9/30 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ConcurrentHashMap 原理解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

了解ConcurrentHashMap 實現原理,建議首先了解下HashMap實現原理。
HashMap 源碼解析(JDK1.8)

為什么要用ConcurrentHashMap

????HashMap線程不安全,而Hashtable是線程安全,但是它使用了synchronized進行方法同步,插入、讀取數據都使用了synchronized,當插入數據的時候不能進行讀取(相當于把整個Hashtable都鎖住了,全表鎖),當多線程并發的情況下,都要競爭同一把鎖,導致效率極其低下。而在JDK1.5后為了改進Hashtable的痛點,ConcurrentHashMap應運而生。

ConcurrentHashMap為什么高效?

JDK1.5中的實現

????ConcurrentHashMap使用的是分段鎖技術,將ConcurrentHashMap將鎖一段一段的存儲,然后給每一段數據配一把鎖(segment),當一個線程占用一把鎖(segment)訪問其中一段數據的時候,其他段的數據也能被其它的線程訪問,默認分配16個segment。默認比Hashtable效率提高16倍。

????ConcurrentHashMap的結構圖如下(網友貢獻的圖,哈):

JDK1.8中的實現

????ConcurrentHashMap取消了segment分段鎖,而采用CAS和synchronized來保證并發安全。數據結構跟HashMap1.8的結構一樣,數組+鏈表/紅黑二叉樹
synchronized只鎖定當前鏈表或紅黑二叉樹的首節點,這樣只要hash不沖突,就不會產生并發,效率又提升N倍。

JDK1.8的ConcurrentHashMap的結構圖如下:

TreeBin: 紅黑二叉樹節點
Node: 鏈表節點

ConcurrentHashMap 源碼分析

ConcurrentHashMap 類結構參照HashMap,這里列出HashMap沒有的幾個屬性。

/*** Table initialization and resizing control. When negative, the* table is being initialized or resized: -1 for initialization,* else -(1 + the number of active resizing threads). Otherwise,* when table is null, holds the initial table size to use upon* creation, or 0 for default. After initialization, holds the* next element count value upon which to resize the table.hash表初始化或擴容時的一個控制位標識量。負數代表正在進行初始化或擴容操作-1代表正在初始化-N 表示有N-1個線程正在進行擴容操作正數或0代表hash表還沒有被初始化,這個數值表示初始化或下一次進行擴容的大小*/private transient volatile int sizeCtl; // 以下兩個是用來控制擴容的時候 單線程進入的變量/*** The number of bits used for generation stamp in sizeCtl.* Must be at least 6 for 32bit arrays.*/private static int RESIZE_STAMP_BITS = 16;/*** The bit shift for recording size stamp in sizeCtl.*/private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;/** Encodings for Node hash fields. See above for explanation.*/static final int MOVED = -1; // hash值是-1,表示這是一個forwardNode節點static final int TREEBIN = -2; // hash值是-2 表示這時一個TreeBin節點

分析代碼主要目的:分析是如果利用CAS和Synchronized進行高效的同步更新數據。
下面插入數據源碼:

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) {//ConcurrentHashMap 不允許插入null鍵,HashMap允許插入一個null鍵if (key == null || value == null) throw new NullPointerException();//計算key的hash值int hash = spread(key.hashCode());int binCount = 0;//for循環的作用:因為更新元素是使用CAS機制更新,需要不斷的失敗重試,直到成功為止。for (Node<K,V>[] tab = table;;) {// f:鏈表或紅黑二叉樹頭結點,向鏈表中添加元素時,需要synchronized獲取f的鎖。Node<K,V> f; int n, i, fh;//判斷Node[]數組是否初始化,沒有則進行初始化操作if (tab == null || (n = tab.length) == 0)tab = initTable();//通過hash定位Node[]數組的索引坐標,是否有Node節點,如果沒有則使用CAS進行添加(鏈表的頭結點),添加失敗則進入下次循環。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}//檢查到內部正在移動元素(Node[] 數組擴容)else if ((fh = f.hash) == MOVED)//幫助它擴容tab = helpTransfer(tab, f);else {V oldVal = null;//鎖住鏈表或紅黑二叉樹的頭結點synchronized (f) {//判斷f是否是鏈表的頭結點if (tabAt(tab, i) == f) {//如果fh>=0 是鏈表節點if (fh >= 0) {binCount = 1;//遍歷鏈表所有節點for (Node<K,V> e = f;; ++binCount) {K ek;//如果節點存在,則更新valueif (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;}}}//TreeBin是紅黑二叉樹節點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) {//如果鏈表長度已經達到臨界值8 就需要把鏈表轉換為樹結構if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i);if (oldVal != null)return oldVal;break;}}}//將當前ConcurrentHashMap的size數量+1addCount(1L, binCount);return null; }
  • 判斷Node[]數組是否初始化,沒有則進行初始化操作
  • 通過hash定位Node[]數組的索引坐標,是否有Node節點,如果沒有則使用CAS進行添加(鏈表的頭結點),添加失敗則進入下次循環。
  • 檢查到內部正在擴容,如果正在擴容,就幫助它一塊擴容。
  • 如果f!=null,則使用synchronized鎖住f元素(鏈表/紅黑二叉樹的頭元素)
    4.1 如果是Node(鏈表結構)則執行鏈表的添加操作。
    4.2 如果是TreeNode(樹型結果)則執行樹添加操作。
  • 判斷鏈表長度已經達到臨界值8 就需要把鏈表轉換為樹結構。
  • 總結:
    ????JDK8中的實現也是鎖分離的思想,它把鎖分的比segment(JDK1.5)更細一些,只要hash不沖突,就不會出現并發獲得鎖的情況。它首先使用無鎖操作CAS插入頭結點,如果插入失敗,說明已經有別的線程插入頭結點了,再次循環進行操作。如果頭結點已經存在,則通過synchronized獲得頭結點鎖,進行后續的操作。性能比segment分段鎖又再次提升。

    本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8????
    點擊這里快速進入簡書

    與50位技術專家面對面20年技術見證,附贈技術全景圖

    總結

    以上是生活随笔為你收集整理的ConcurrentHashMap 原理解析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。