探究ConcurrentHashMap中鍵值對在Segment[]的下標如何確定

来源:https://www.cnblogs.com/lonelyJay/archive/2018/10/08/9736027.html
-Advertisement-
Play Games

內容 本文對JDK1.7下使用segmentShift和segmentMask求解ConcurrentHashMap鍵值對在Segment[]中的下標值進行了探究和論證。 適合人群 ​ Java進階 說明 轉載請註明出處,尊重筆者的勞動成果。 推薦閱讀 探究HashMap線性不安全(二)——鏈表成環 ...


內容

   本文對JDK1.7下使用segmentShift和segmentMask求解ConcurrentHashMap鍵值對在Segment[]中的下標值進行了探究和論證。

適合人群

 ​  Java進階

說明

   轉載請註明出處,尊重筆者的勞動成果。

 推薦閱讀

   探究HashMap線性不安全(二)——鏈表成環的詳細過程

 正文

   下麵先查看ConcurrentHashMap源碼中的put操作,找到segment[]的下標j的計算公式。

 1 @SuppressWarnings("unchecked")
 2 public V put(K key, V value) {
 3     Segment<K,V> s;
 4     if (value == null)
 5         throw new NullPointerException();
 6     int hash = hash(key);
 7     //key對應的segment[]的下標j的計算公式
 8     int j = (hash >>> segmentShift) & segmentMask;
 9     if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
10          (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
11         s = ensureSegment(j);
12     return s.put(key, hash, value, false);
13 }

  由上面的ConcurrentHashMap源碼可知,一個鍵值對在Segment數組中下標j的計算公式為:

j = (hash >>> segmentShift) & segmentMask

  公式雖然不長,但是它包含了2個“晦澀難懂”的參數:segmentShift和segmentMask ,讓人費解。下麵筆者用一種通俗簡單的方式來解釋該公式的含義。

  首先,閱讀ConcurrentHashMap的構造方法,重點查看註釋區域,其中包含了segmentShift和segmentMask的定義:

segmentShift = 32 - sshift;segmentMask = ssize - 1;

  以及segment數組長度ssize與sshift的關係:

2^sshif=ssize
  ConcurrentHashMap的構造方法
 1 public ConcurrentHashMap(int initialCapacity,
 2                                float loadFactor, int concurrencyLevel) {
 3           if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
 4               throw new IllegalArgumentException();
 5           if (concurrencyLevel > MAX_SEGMENTS)
 6               concurrencyLevel = MAX_SEGMENTS;
 7          int sshift = 0;
 8          int ssize = 1;
 9      //2^sshif=ssize,例:sshift=4,ssize=16;
10    //根據concurrentLevel計算得出ssize為segments數組長度
11          while (ssize < concurrencyLevel) {
12              ++sshift;
13              ssize <<= 1;
14          }
15          //segmentShift和segmentMask的定義
16          this.segmentShift = 32 - sshift;
17          this.segmentMask = ssize - 1;
18          if (initialCapacity > MAXIMUM_CAPACITY)
19              initialCapacity = MAXIMUM_CAPACITY;
20          //計算cap的大小,即Segment中HashEntry的數組長度,cap也一定為2的n次方.
21          int c = initialCapacity / ssize;
22          if (c * ssize < initialCapacity)
23              ++c;
24          int cap = MIN_SEGMENT_TABLE_CAPACITY;
25          while (cap < c)
26              cap <<= 1;
27          //創建segments數組並初始化第一個Segment,其餘的Segment延遲初始化
28          Segment<K,V> s0 =
29              new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
30                              (HashEntry<K,V>[])new HashEntry[cap]);
31          Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
32          UNSAFE.putOrderedObject(ss, SBASE, s0); 
33          this.segments = ss;
34      }

   由此可知,求key散列到長度為ssize的Segment數組的下標j,必定有下標j的值域為[0,ssize-1]。由於ssize=2^sshif,那麼小標j可以用1個sshift位的二進位數字表示。假如:ssize為16,那麼sshift=4,j的值域為[0,15],而[0000b,1111b]就是j的值域;則求key在Segment[]的下標j,就是求key對應的一個散列的4位二進位數值。而ConcurrentHashMap的源碼求下標j的方式非常簡單,就是取key的hash值的高4位。因此,求key散列到長度為ssize的Segment數組的下標j,就是求key的hash值的高sshift位。

  故有,j=(key.hash>>>(32-sshift))&(ssize-1)。而由源碼可知,segmentShift = 32 - sshift,segmentMask = ssize - 1。即:

j=(key.hash>>>(32-sshift))&(ssize-1)=(key.hash>>>segmentShift )&segmentMask。(其中>>>表示無符號右移,空位補0)

  以ssize為16為例,演示計算過程:

1538405541238


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 結對編程項目在歡快的國慶假期中也順利結束了。從最初拿到結對編程項目的思考,再到一步一步實現,中間經歷了一個漫長的過程。在我和隊友的多次協商下,最終我們還是選擇使用基於python來實現這一次結對編程項目,並且最終選擇了以eric6 + pyqt5 +Anaconda3以及pycharm混搭的開發環境 ...
  • 如果你之前從來沒有使用過面向對象編程語言,那麼在學習Java之前需要先理解幾個有關面向對象編程的基本概念。這篇教程將會向你介紹對象、類、集成、介面和包的概念,以及這些概念是如何與現實世界相關聯,並介紹這些概念在Java語言中的體現。 ...
  • socket API 調用後的錯誤判斷 perror errno 調用完socket API後,需要判斷調用是否成功與失敗。如果失敗,會自動設置errno(是個整數), 並且用perror可以列印出具體的錯誤信息。 註意點: 1,如果有多個socket API調用失敗,errno存放的是最後一個失敗 ...
  • 在考慮一個結果的概率時候,要考慮眾多的屬性,貝葉斯演算法利用所有可能的數據來進行修正預測,如果大量的特征產生的影響較小,放在一起,組合的影響較大,適合於朴素貝葉斯分類 導入類庫 代碼 ...
  •  上次接手一個項目需要整合公眾號、小程式以及APP的用戶,查閱了微信文檔以及一些作者的文章,中間踩了不少坑,在此記錄一下解決的流程。 要點  實現統一信息的有以下幾點:  1. 在微信開放平臺綁定需要統一信息的應用;  2. 公眾號採用以snsapi_user ...
  • 二叉樹的深度: 輸入一棵二叉樹,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度為樹的深度。 思路: 1.非遞歸層序遍歷 2.使用輔助隊列,根結點先入隊列 3. 迴圈判斷隊列是否為空,如果不為空就繼續迴圈隊列裡面的每個結點 4. 迴圈隊列時,當前當前結點出... ...
  • 題目 一直沒有頭緒的一道題 明明只有普及-難度 看了看題解用的大多是方程和Fibonacci 於是更加懵逼了。。。 今天立志AC此題 結果用大模擬還真過了 說一下思路 然而並沒有思路 按照題意敲代碼 不妨設第二站上下車了k人 分別統計每一站a和k的繫數 1、2、n站特殊處理 然後求出k,代入x站中輸 ...
  • Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-f8IeEI/MYSQL-python/錯誤無法安裝python第三方庫需要安裝libmysqlclient-devapt-get inst... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...