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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

漫画:什么是ConcurrentHashMap

發布時間:2023/12/3 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 漫画:什么是ConcurrentHashMap 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載自?玻璃貓 程序員小灰









————————————



























————————————







前兩期我們講解了HashMap的基本原理,以及高并發場景下存在的問題。沒看過的小伙伴可以點擊下面鏈接:


漫畫:什么是HashMap?

漫畫:高并發下的HashMap


如果實在懶得看也沒有關系,我們來簡單回顧一下HashMap的結構:




簡單來說,HashMap是一個Entry對象的數組。數組中的每一個Entry元素,又是一個鏈表的頭節點。


Hashmap不是線程安全的。在高并發環境下做插入操作,有可能出現下面的環形鏈表:


















Segment是什么呢?Segment本身就相當于一個HashMap對象。


同HashMap一樣,Segment包含一個HashEntry數組,數組中的每一個HashEntry既是一個鍵值對,也是一個鏈表的頭節點。


單一的Segment結構如下:




像這樣的Segment對象,在ConcurrentHashMap集合中有多少個呢?有2的N次方個,共同保存在一個名為segments的數組當中。


因此整個ConcurrentHashMap的結構如下:





可以說,ConcurrentHashMap是一個二級哈希表。在一個總的哈希表下面,有若干個子哈希表。


這樣的二級結構,和數據庫的水平拆分有些相似。









Case1:不同Segment的并發寫入





不同Segment的寫入是可以并發執行的。



Case2:同一Segment的一寫一讀





同一Segment的寫和讀是可以并發執行的。



Case3:同一Segment的并發寫入





Segment的寫入是需要上鎖的,因此對同一Segment的并發寫入會被阻塞。



由此可見,ConcurrentHashMap當中每個Segment各自持有一把鎖。在保證線程安全的同時降低了鎖的粒度,讓并發操作效率更高。







Get方法:


1.為輸入的Key做Hash運算,得到hash值。


2.通過hash值,定位到對應的Segment對象


3.再次通過hash值,定位到Segment當中數組的具體位置。



Put方法:


1.為輸入的Key做Hash運算,得到hash值。


2.通過hash值,定位到對應的Segment對象


3.獲取可重入鎖


4.再次通過hash值,定位到Segment當中數組的具體位置。


5.插入或覆蓋HashEntry對象。


6.釋放鎖。







Size方法的目的是統計ConcurrentHashMap的總元素數量, 自然需要把各個Segment內部的元素數量匯總起來。


但是,如果在統計Segment元素數量的過程中,已統計過的Segment瞬間插入新的元素,這時候該怎么辦呢?











ConcurrentHashMap的Size方法是一個嵌套循環,大體邏輯如下:


1.遍歷所有的Segment。


2.把Segment的元素數量累加起來。


3.把Segment的修改次數累加起來。


4.判斷所有Segment的總修改次數是否大于上一次的總修改次數。如果大于,說明統計過程中有修改,重新統計,嘗試次數+1;如果不是。說明沒有修改,統計結束。


5.如果嘗試次數超過閾值,則對每一個Segment加鎖,再重新統計。


6.再次判斷所有Segment的總修改次數是否大于上一次的總修改次數。由于已經加鎖,次數一定和上次相等。


7.釋放鎖,統計結束。



官方源代碼如下:


public int size() {// Try a few times to get accurate count. On failure due to ? // continuous async changes in table, resort to locking. ? final Segment<K,V>[] segments = this.segments;int size;boolean overflow; // true if size overflows 32 bits ? ?long sum; // sum of modCounts ? ?long last = 0L; // previous sum ? ?int retries = -1; // first iteration isn't retry ? ?try {for (;;) {if (retries++ == RETRIES_BEFORE_LOCK) {for (int j = 0; j < segments.length; ++j)ensureSegment(j).lock(); // force creation ? ? ? ? ? ?}sum = 0L;size = 0;overflow = false;for (int j = 0; j < segments.length; ++j) {Segment<K,V> seg = segmentAt(segments, j);if (seg != null) {sum += seg.modCount;int c = seg.count;if (c < 0 || (size += c) < 0)overflow = true;}}if (sum == last)break;last = sum;}} finally {if (retries > RETRIES_BEFORE_LOCK) {for (int j = 0; j < segments.length; ++j)segmentAt(segments, j).unlock();}}return overflow ? Integer.MAX_VALUE : size; }


為什么這樣設計呢?這種思想和樂觀鎖悲觀鎖的思想如出一轍。


為了盡量不鎖住所有Segment,首先樂觀地假設Size過程中不會有修改。當嘗試一定次數,才無奈轉為悲觀鎖,鎖住所有Segment保證強一致性。





幾點說明:


1. 這里介紹的ConcurrentHashMap原理和代碼,都是基于Java1.7的。在Java8中會有些許差別。


2.ConcurrentHashMap在對Key求Hash值的時候,為了實現Segment均勻分布,進行了兩次Hash。有興趣的朋友可以研究一下源代碼。




—————END—————




總結

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

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