摘要:如果沖突了,同步頭節(jié)點(diǎn),進(jìn)行鏈表操作,如果鏈表長度達(dá)到,分成紅黑樹。每次加入一個(gè)線程都會(huì)將的低位加一。擴(kuò)容最大的幫助線程是,這是低位的最大值限制的。線程處理完之后,如果沒有可選區(qū)間,且任務(wù)沒有完成,就會(huì)將整個(gè)表檢查一遍,防止遺漏。
前言
每一次總結(jié)都意味著重新開始,同時(shí)也是為了更好的開始。ConcurrentHashMap 一直是我心中的痛。雖然不敢說完全讀懂了,但也看了幾個(gè)重要的方法,有不少我覺得比較重要的知識(shí)點(diǎn)。
然后呢,放一些樓主寫的關(guān)于 ConcurrentHashMap 相關(guān)源碼分析的文章鏈接:
ConcurrentHashMap 擴(kuò)容分析拾遺
并發(fā)編程——ConcurrentHashMap#addCount() 分析
并發(fā)編程——ConcurrentHashMap#transfer() 擴(kuò)容逐行分析
并發(fā)編程——ConcurrentHashMap#helpTransfer() 分析
并發(fā)編程 —— ConcurrentHashMap size 方法原理分析
并發(fā)編程之 ConcurrentHashMap(JDK 1.8) putVal 源碼分析
深入理解 HashMap put 方法(JDK 8逐行剖析)
深入理解 hashcode 和 hash 算法
putVal 方法總結(jié)說起 ConcurrentHashMap ,當(dāng)然從入口開始說。該方法要點(diǎn)如下:
不允許有 null key 和 null value。
只有在第一次 put 的時(shí)候才初始化 table。初始化有并發(fā)控制。通過 sizeCtl 變量判斷(小于 0)。
當(dāng) hash 對(duì)應(yīng)的下標(biāo)是 null 時(shí),使用 CAS 插入元素。
當(dāng) hash 對(duì)應(yīng)的下標(biāo)值是 forward 時(shí),幫助擴(kuò)容,但有可能幫不了,因?yàn)槊總€(gè)線程默認(rèn) 16 個(gè)桶,如果只有 16個(gè)桶,第二個(gè)線程是無法幫助擴(kuò)容的。
如果 hash 沖突了,同步頭節(jié)點(diǎn),進(jìn)行鏈表操作,如果鏈表長度達(dá)到 8 ,分成紅黑樹。
調(diào)用 addCount 方法,對(duì) size 加一,并判斷是否需要擴(kuò)容(如果是覆蓋,就不調(diào)用該方法)。
Cmap 的并發(fā)性能是 hashTable 的 table.length 倍。只有出現(xiàn)鏈表才會(huì)同步,否則使用 CAS 插入。性能極高。
size 方法總結(jié)size 方法不準(zhǔn)確,原因是由于并發(fā)插入,baseCount 難以及時(shí)更新。計(jì)數(shù)盒子也難以及時(shí)更新。
內(nèi)部通過兩個(gè)變量,一個(gè)是 baseCount,一個(gè)是 counterCells,counterCells 是并發(fā)修改 baseCount 后的備用方案。
具體更新 baseCount 和 counterCells 是在 addCount 方法中。備用方法 fullAddCount 則會(huì)死循環(huán)插入。
CounterCell 是一個(gè)用于分配計(jì)數(shù)的填充單元,改編自 LongAdder和Striped64。內(nèi)部只有一個(gè) volatile 的 value 變量,同時(shí)這個(gè)類標(biāo)記了 @sun.misc.Contended ,這是一個(gè)避免偽共享的注解,用于替代之前的緩存行填充。多線程情況下,注解讓性能提升 5 倍。
helpTransfer 方法總結(jié)當(dāng) Cmap 嘗試插入的時(shí)候,發(fā)現(xiàn)該節(jié)點(diǎn)是 forward 類型,則會(huì)幫助其擴(kuò)容。
每次加入一個(gè)線程都會(huì)將 sizeCtl 的低 16 位加一。同時(shí)會(huì)校驗(yàn)高 16 位的標(biāo)示符。
擴(kuò)容最大的幫助線程是 65535,這是低 16 位的最大值限制的。
每個(gè)線程默認(rèn)分配 16 個(gè)桶,如果桶的數(shù)量是 16,那么第二個(gè)線程無法幫助其擴(kuò)容。
transfer 方法總結(jié)該方法會(huì)根據(jù) CPU 核心數(shù)平均分配給每個(gè) CPU 相同數(shù)量的桶。但如果不夠 16 個(gè),默認(rèn)就是 16 個(gè)。
擴(kuò)容是按照 2 倍進(jìn)行擴(kuò)容。
每個(gè)線程在處理完自己領(lǐng)取的區(qū)間后,還可以繼續(xù)領(lǐng)取,如果有的話。這個(gè)是 transferIndex 變量遞減 16 實(shí)現(xiàn)的。
每次處理空桶的時(shí)候,會(huì)插入一個(gè) forward 節(jié)點(diǎn),告訴 putVal 的線程:“我正在擴(kuò)容,快來幫忙”。但如果只有 16 個(gè)桶,只能有一個(gè)線程擴(kuò)容。
如果有了占位符,那就不處理,跳過這個(gè)桶。
如果有真正的實(shí)際值,那就同步頭節(jié)點(diǎn),防止 putVal 那里并發(fā)。
同步塊里會(huì)將鏈表拆成兩份,根據(jù) hash & length 得到是否是 0,如果是0,放在低位,反之,反之放在 length + i 的高位。這里的設(shè)計(jì)是為了防止下次取值的時(shí)候,hash 不到正確的位置。
如果該桶的類型是紅黑樹,也會(huì)拆成 2 個(gè),這是必須的。然后判斷拆分過的桶的大小是否小于等于 6,如果是,改成鏈表。
線程處理完之后,如果沒有可選區(qū)間,且任務(wù)沒有完成,就會(huì)將整個(gè)表檢查一遍,防止遺漏。
addCount 方法總結(jié)當(dāng)插入結(jié)束的時(shí)候,會(huì)對(duì) size 進(jìn)行加一。也會(huì)進(jìn)行是否需要擴(kuò)容的判斷。
優(yōu)先使用計(jì)數(shù)盒子(如果不是空,說明并發(fā)了),如果計(jì)數(shù)盒子是空,使用 baseCount 變量。對(duì)其加 X。
如果修改 baseCount 失敗,使用計(jì)數(shù)盒子。如果此次修改失敗,在另一個(gè)方法死循環(huán)插入。
檢查是否需要擴(kuò)容。
如果 size 大于等于 sizeCtl 閾值,且長度小于 1 << 30,可以擴(kuò)容成 1 << 30,但不能擴(kuò)容成 1 << 31。
如果已經(jīng)在擴(kuò)容,幫助其擴(kuò)容,和 helpTransfer 邏輯一樣。
如果沒有在擴(kuò)容,自行開啟擴(kuò)容,更新 sizeCtl 變量為負(fù)數(shù),賦值為標(biāo)識(shí)符高 16 位 + 2。
小結(jié)ConcurrentHashMap 滿是財(cái)富,都是精華代碼,我們這次閱讀只是管中窺豹,要知道其中包含 53 個(gè)類,6300 行代碼,但這次確實(shí)收獲很多。有時(shí)間一定再次閱讀!!
能力不高,水平有限,有些地方確實(shí)理解不了 Doug Lea 大師的設(shè)計(jì),如果有什么錯(cuò)誤,還請(qǐng)大家指出。不勝感激。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/69497.html
摘要:下面我來簡單總結(jié)一下的核心要點(diǎn)底層結(jié)構(gòu)是散列表數(shù)組鏈表紅黑樹,這一點(diǎn)和是一樣的。是將所有的方法進(jìn)行同步,效率低下。而作為一個(gè)高并發(fā)的容器,它是通過部分鎖定算法來進(jìn)行實(shí)現(xiàn)線程安全的。 前言 聲明,本文用的是jdk1.8 前面章節(jié)回顧: Collection總覽 List集合就這么簡單【源碼剖析】 Map集合、散列表、紅黑樹介紹 HashMap就是這么簡單【源碼剖析】 LinkedHas...
摘要:中的使用及在中的沖突方案引言簡稱是在作為的替代選擇新引入的,是包的重要成員。為了解決在頻繁沖突時(shí)性能降低的問題,中使用平衡樹來替代鏈表存儲(chǔ)沖突的元素。目前,只有和會(huì)在頻繁沖突的情況下使用平衡樹。 java中ConcurrentHashMap的使用及在Java 8中的沖突方案 1、引言 ConcurrentHashMap(簡稱CHM)是在Java 1.5作為Hashtable的替代選擇新...
摘要:若遇到哈希沖突,則將沖突的值加到鏈表中即可。之后相比于之前的版本,之后在解決哈希沖突時(shí)有了較大的變化,當(dāng)鏈表長度大于閾值默認(rèn)為時(shí),將鏈表轉(zhuǎn)化為紅黑樹,以減少搜索時(shí)間。有序,唯一紅黑樹自平衡的排序二叉樹。 本文是最最最常見Java面試題總結(jié)系列第三周的文章。主要內(nèi)容: Arraylist 與 LinkedList 異同 ArrayList 與 Vector 區(qū)別 HashMap的底層...
摘要:今天主要講解的是本文力求簡單講清每個(gè)知識(shí)點(diǎn),希望大家看完能有所收獲一和回顧線程安全的和我們知道是用于替代的,是線程安全的容器。使用迭代器遍歷時(shí)不需要顯示加鎖,看看與方法的實(shí)現(xiàn)可能就有點(diǎn)眉目了。 前言 只有光頭才能變強(qiáng) showImg(https://segmentfault.com/img/remote/1460000016931828?w=1120&h=640); 前一陣子寫過一篇C...
摘要:從而能夠進(jìn)一步深入了解框架。至此我們框架開發(fā)完成。雖然說閱讀源碼是了解框架的最終手段。但是框架作為一個(gè)生產(chǎn)框架,為了保證通用和穩(wěn)定,源碼必定是高度抽象,且處理大量細(xì)節(jié)。下一篇文章應(yīng)該會(huì)是徒手?jǐn)]框架實(shí)現(xiàn)。 原文地址:https://www.xilidou.com/2018/... Spring 作為 J2ee 開發(fā)事實(shí)上的標(biāo)準(zhǔn),是每個(gè)Java開發(fā)人員都需要了解的框架。但是Spring 的...
閱讀 1768·2021-10-11 10:57
閱讀 2352·2021-10-08 10:14
閱讀 3393·2019-08-29 17:26
閱讀 3340·2019-08-28 17:54
閱讀 3021·2019-08-26 13:38
閱讀 2885·2019-08-26 12:19
閱讀 3608·2019-08-23 18:05
閱讀 1277·2019-08-23 17:04