日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

淺談Java源碼ConcurrentHashMap

瀏覽:182日期:2022-08-12 13:37:11
目錄一、記錄形式二、ConcurrentHashMap三、關(guān)鍵點(diǎn)一、記錄形式

打算直接把過程寫在源碼中,會(huì)按序進(jìn)行注釋,查閱的時(shí)候可以按序號(hào)只看注釋部分

二、ConcurrentHashMap

直接模擬該類的使用過程,從而一步步看其怎么運(yùn)作的吧,當(dāng)然最好還是帶著問題一遍思考一遍總結(jié)會(huì)比較好,我閱讀源碼的時(shí)候帶著以下幾個(gè)問題

并發(fā)體現(xiàn)在哪里?怎么保證線程安全的 怎么擴(kuò)容的?擴(kuò)容是怎么保證線程安全的? 怎么put的?put是怎么保證線程安全的? 用了哪些鎖?這些鎖的作用是什么? 需要留意哪些關(guān)鍵點(diǎn)?

我們最簡(jiǎn)單地使用方法是怎么樣的?

new一個(gè)ConcurrentHashMap對(duì)象 調(diào)用put方法放入k-v對(duì) 調(diào)用get方法獲取k-v對(duì)

那么很顯然,考慮只有在進(jìn)行修改與更新時(shí)需要考慮并發(fā),所以我關(guān)注的重點(diǎn)在put方法與擴(kuò)容上

首先new一個(gè)對(duì)象時(shí),我們傳參入一個(gè)size,調(diào)用其只有一個(gè)參數(shù)的構(gòu)造器

public ConcurrentHashMap(int initialCapacity) {if (initialCapacity < 0) throw new IllegalArgumentException();// 1、判斷傳入的大小是否超過最大值的一半,若超過則取最大值// 2、若沒超過,則調(diào)用tableSizeFor// 3、tableSizeFor可以讓size為2的倍數(shù),為什么要是2的倍數(shù)呢?因?yàn)閷?duì)hash取模的時(shí)候// 用的是位運(yùn)算,只有size為2的倍數(shù)才能這么做int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));this.sizeCtl = cap; }

// 1、判斷傳入的大小是否超過最大值的一半,若超過則取最大值// 2、若沒超過,則調(diào)用tableSizeFor// 3、tableSizeFor讓size為2的倍數(shù),為什么要是2的倍數(shù)呢?因?yàn)閷?duì)hash取模的時(shí)候// 用的是位運(yùn)算,只有size為2的倍數(shù)才能這么做

注意,此時(shí)并沒有創(chuàng)建map數(shù)據(jù)結(jié)構(gòu),所以ConcurrentHashMap是懶惰創(chuàng)建的

接著我們調(diào)用put方法放入數(shù)據(jù),put方法調(diào)的putVal

final V putVal(K key, V value, boolean onlyIfAbsent) { // 1、k-v都不能為空,不然拋異常if (key == null || value == null) throw new NullPointerException();// 2、獲取key的hashcode的hash值int hash = spread(key.hashCode());// 3、使用binCount來統(tǒng)計(jì)鏈表有多少個(gè)元素的,便于后面判斷是否需要變成紅黑樹int binCount = 0;// 4、創(chuàng)建tab臨時(shí)變量,并賦值為table,由于還沒初始化,值為null// 這里注意了,table的類型是Node數(shù)組,這個(gè)Node其實(shí)就是Map.Entry<K,V>for (Node<K,V>[] tab = table;;) { Node<K,V> f; int n, i, fh; // 5、判斷tab為空后才進(jìn)行初始化,初始化完成后重新進(jìn)入循環(huán) if (tab == null || (n = tab.length) == 0)tab = initTable(); // 6、此時(shí)已經(jīng)初始化好了,可以開始插入數(shù)據(jù)。 // 這里用tabAt(tab,i)獲取tab的第i個(gè)下標(biāo)上的鏈表指針 // 注意了,(n-1)& hash其實(shí)就是hash%n,只有n為2的次方才能這么做 // 位運(yùn)算可以提升效率 // 7、f就是獲取到的第i個(gè)位置的鏈表頭指針 // 如果為null說明什么都沒有,可以嘗試插入元素 else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // 8、考慮插入那就要考慮并發(fā)了,casTab表示用cas方法進(jìn)行插入 // 插入一個(gè)新的Node結(jié)點(diǎn),這個(gè)是能夠保證線程安全的 // 我們知道保證線程安全除了用cas之外還不夠,還需要保證操作對(duì)象的可見性 // 在這里是對(duì)tab進(jìn)行操作,tab在前面用table賦值,而table是加了volatile的 // 所以沒毛病哈if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null))) break; // no lock when adding to empty bin } // 9、如果f不為空,并且f.hash==MOVED(-1),說明當(dāng)前這個(gè)位置正在被移動(dòng) // 說明有線程在擴(kuò)容,那么當(dāng)然不能這時(shí)候還去插入了,這里調(diào)用helpTransfer去幫助擴(kuò)容 // 注意了,這意味著擴(kuò)容時(shí)是一個(gè)一個(gè)位置來移動(dòng)的,每移動(dòng)一個(gè)就將f.hash改成MOVED // 也就意味著如果當(dāng)前線程想要操作的位置還沒有被移動(dòng)時(shí)是可以操作的,這使得并發(fā)度更高了 else if ((fh = f.hash) == MOVED)tab = helpTransfer(tab, f); // 10、如果f.hash表示沒有被移動(dòng),且f不為null說明可以這個(gè)位置已經(jīng)有東西了 // 需要對(duì)其遍歷,并找到合適的位置插入 else {V oldVal = null;// 11、由于要進(jìn)行插入了,所以得加鎖,注意了哈,這里用的synchronized// 并且鎖住的是當(dāng)前位置對(duì)象(不一定是鏈表也可能是樹)// 意味著除了當(dāng)前線程,其他線程都不準(zhǔn)操作了哈// 如果這時(shí)候正在擴(kuò)容的線程擴(kuò)到這里了,會(huì)被阻塞的哈synchronized (f) {// 確定f為起點(diǎn) if (tabAt(tab, i) == f) { // 12、判斷f.hash是大于0,大于0表示當(dāng)前這個(gè)東西是鏈表 // 下面執(zhí)行鏈表的更新操作if (fh >= 0) { binCount = 1; // 13、接著就是具體的遍歷鏈表,查找是否對(duì)應(yīng)值是否存在 // 如果遍歷完都找不到,那么就在尾部插入新的結(jié)點(diǎn) // 注意了哈這就是尾插 // 14、同時(shí),每遍歷一個(gè)結(jié)點(diǎn)還要累加binCount // 即統(tǒng)計(jì)一下當(dāng)前鏈表個(gè)數(shù),便于后面轉(zhuǎn)紅黑樹判斷 for (Node<K,V> e = f;; ++binCount) {K ek;if (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;} }}// 13、如果f對(duì)應(yīng)的是樹結(jié)構(gòu),那就執(zhí)行樹的更新方法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; }} }}// 14、前面說了哈,binCount就是鏈表元素個(gè)數(shù),接著就判斷是否大于閾值// 大于則轉(zhuǎn)樹,可以看這個(gè)閾值TREEIFY_THRESHOLD=8if (binCount != 0) { if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i); if (oldVal != null)return oldVal; break;} }}// 15、addCount是讓size+1的// 這里要注意,加法也是分多步的,需要先get才能+,因此為了保證線程安全也是需要用cas的// 這里加size的方式并不是直接往size上加,因?yàn)閟ize是經(jīng)常修改的,如果經(jīng)常訪問的話效率很低// 那么做法和LongAdder這一原子累加器類似,用一個(gè)CountCell數(shù)組,每個(gè)線程只操作數(shù)組中的// 某一個(gè)Cell,最后匯總即可addCount(1L, binCount);return null; }

總結(jié)一下put的過程

1.先判斷map是否創(chuàng)建,沒創(chuàng)建則先創(chuàng)建,結(jié)構(gòu)為 Node<K,V>[ ] extend Map.Entry

2.接著找key應(yīng)該放在哪個(gè)位置 i

3.判斷該位置是否為空,為空則使用CAS插入一個(gè)新的Node

4.不為空則判斷當(dāng)前位置狀態(tài)是否為MOVED,是則說明當(dāng)前位置正在被其他線程改動(dòng),當(dāng)前線程需要幫助完成移動(dòng)

5.不為空且不為MOVED,則判斷是鏈表還是樹,分別執(zhí)行對(duì)應(yīng)的更新方法

6.如果為鏈表

先對(duì)鏈表上鎖,用syn 則遍歷并查找是否已存在 找到最后都不存在則直接尾插 同時(shí)統(tǒng)計(jì)鏈表上的元素

7.判斷鏈表元素是否超過變成樹的閾值 8 ,超過則直接變成樹,變成樹也是加syn

8.使用addCount更新size,更新方式類似LongAdder

三、關(guān)鍵點(diǎn) 懶惰加載map 對(duì)當(dāng)前位置操作之前需要判斷當(dāng)前位置的存儲(chǔ)的內(nèi)容是否被其他線程移動(dòng)了,如果被移動(dòng)則先去幫助完成移動(dòng)

執(zhí)行擴(kuò)容移動(dòng)的線程是挨個(gè)移動(dòng)每個(gè)位置的鏈表,移動(dòng)前會(huì)先將當(dāng)前位置的狀態(tài)用CAS改成MOVED

注意了這個(gè)是提升并發(fā)度的關(guān)鍵所在

因?yàn)椴迦牒鸵苿?dòng)(擴(kuò)容)的粒度是細(xì)化到每個(gè)位置的鏈表上

意味著擴(kuò)容和插入可以同時(shí)進(jìn)行

意味著插入時(shí)上鎖后,擴(kuò)容線程執(zhí)行到該位置需要阻塞

而不是直接整個(gè)map都鎖住

當(dāng)前位置沒內(nèi)容時(shí),通過CAS插入新Node,而操作鏈表時(shí)用的是syn

如果面試問到ConcurrentHashMap中用了什么鎖就心中有數(shù)了

更新size的時(shí)候用的是LongAdder類似的方法

有一個(gè)CountCell數(shù)組,每個(gè)線程更新后,對(duì)數(shù)組中的某個(gè)Cell+1

如果沒有競(jìng)爭(zhēng)則只有一個(gè)baseCell,對(duì)其+1

統(tǒng)計(jì)size時(shí)匯總即可

再細(xì)化一下前面的流程思考初始化map的時(shí)候怎么保證線程安全?防止多個(gè)線程同時(shí)初始化?答案在initTable方法中

可以看到,sizeCtl如果是負(fù)數(shù)說明正在擴(kuò)容或者初始化 因此當(dāng)需要初始化時(shí),就去CAS地改變sizeCtl,將其變?yōu)樨?fù)數(shù) 哪個(gè)線程CAS成功,則可以執(zhí)行初始化,這就保證了線程安全 可以再去看看,sizeCtl是volatile修飾的哈 并且SIZECTL是sizeCtl的offset,這些都是原子類類似的東西了

private final Node<K,V>[] initTable() {Node<K,V>[] tab; int sc;while ((tab = table) == null || tab.length == 0) { if ((sc = sizeCtl) < 0)Thread.yield(); // lost initialization race; just spin else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {try { if ((tab = table) == null || tab.length == 0) {int n = (sc > 0) ? sc : DEFAULT_CAPACITY;@SuppressWarnings('unchecked')Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];table = tab = nt;sc = n - (n >>> 2); }} finally { sizeCtl = sc;}break; }}return tab; }

get方法就不說了,因?yàn)椴簧婕安l(fā),就是查找而已感覺差不多了,把put方法搞清楚了,ConcurrentHashMap怎么解決線程安全的也清楚了,并且并發(fā)關(guān)鍵點(diǎn)在哪也清楚了

到此這篇關(guān)于淺談Java源碼ConcurrentHashMap的文章就介紹到這了,更多相關(guān)Java ConcurrentHashMap內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Java
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
激情欧美亚洲| 在线天堂资源www在线污| 久久影院一区| 久久蜜桃精品| 91精品国产乱码久久久久久久| 97精品中文字幕| 国产成人精品免费视| 97se综合| 久久久久亚洲| 久久国产免费| 在线视频日韩| 亚洲精品乱码久久久久久蜜桃麻豆| 亚洲人成精品久久久| 国产伦乱精品| 麻豆mv在线观看| 蜜臀av免费一区二区三区| 三级亚洲高清视频| 国产欧美三级| 日韩在线欧美| 亚洲性视频h| 亚洲欧美日韩国产| 日韩 欧美一区二区三区| 卡一卡二国产精品| 亚洲一级特黄| 亚洲毛片在线免费| 免费视频一区二区三区在线观看| 另类专区亚洲| 亚洲欧美久久久| 国产情侣一区在线| 日韩欧美字幕| 视频一区在线播放| 美女视频黄免费的久久| 久久久久蜜桃| 日韩高清在线不卡| 日韩精品中文字幕第1页| 国产精品日本| 国产精品欧美大片| 欧美日韩在线观看视频小说| 亚洲一区欧美| 精品精品99| 欧美精品黄色| 国产欧美激情| 欧美a级一区| 国产精品综合色区在线观看| 91精品亚洲| 日韩1区2区日韩1区2区| 欧美aa在线观看| 免费成人在线观看| 精品一区二区三区视频在线播放| 好吊日精品视频| 久久不见久久见免费视频7| 五月婷婷六月综合| 国产精品欧美大片| 伊人久久亚洲美女图片| 欧美国产中文高清| 久久在线视频免费观看| 国产日韩欧美中文在线| 亚洲天堂久久| 国产精品99精品一区二区三区∴| 免费精品国产的网站免费观看| 国产欧美日韩免费观看| 在线成人直播| 国产一区福利| 日韩精品第一| 婷婷综合五月| 欧美xxxx中国| 日韩在线麻豆| 久久理论电影| 精品亚洲成人| 日韩精品视频一区二区三区| 国产精品二区不卡| 日本aⅴ精品一区二区三区 | 黄色成人在线网址| 另类综合日韩欧美亚洲| 综合激情网站| 欧美二区视频| 国产成人精品一区二区三区免费 | 麻豆一区二区三区| 男女男精品视频网| 波多视频一区| 国产精品v亚洲精品v日韩精品| 免费在线欧美视频| 激情久久婷婷| 国产一区二区三区成人欧美日韩在线观看 | 婷婷久久一区| 国产精品精品| 国产极品模特精品一二| 亚洲色图网站| 欧美日韩中文字幕一区二区三区| 欧美国产另类| 日韩国产欧美三级| 蜜臀久久久99精品久久久久久| 91tv亚洲精品香蕉国产一区| 国产精品一卡| 日韩1区2区3区| 蜜桃一区二区三区在线观看| 精品一区亚洲| 久久久精品日韩| 欧美激情另类| 久久精品一区二区国产| 欧美亚洲专区| 日韩av中文字幕一区二区| 中文字幕日本一区| 欧美日韩国产免费观看| 亚洲成av在线| 人人草在线视频| 理论片午夜视频在线观看| 久久精品国产99国产| 国产精品扒开腿做爽爽爽软件| 奇米777国产一区国产二区| 亚洲精品福利| 日韩 欧美一区二区三区| 婷婷综合成人| 日本欧美韩国一区三区| 日本午夜精品久久久| 日韩av网站在线观看| 日韩av中文字幕一区| 青青国产91久久久久久| 日韩高清电影一区| 久久精品99国产精品| 国产精品观看| 成人午夜网址| 日韩欧美一区二区三区免费观看| 神马午夜久久| 欧美精品自拍| 在线一区二区三区视频| 午夜久久av | 综合一区av| 日韩激情中文字幕| 国产亚洲电影| 麻豆成人在线观看| 天堂av在线| 久久久影院免费| 亚洲女同一区| 亚洲欧美日本国产| 国产日产精品一区二区三区四区的观看方式| 青青草精品视频| 久久97久久97精品免视看秋霞| 精品视频91| 亚洲va在线| 性欧美69xoxoxoxo| 亚洲久久一区| 欧美激情麻豆| 成人福利av| 在线亚洲国产精品网站| 日韩欧美美女在线观看| 国产欧美二区| 国产一区二区亚洲| 999久久久国产精品| 老鸭窝毛片一区二区三区| 日本中文字幕不卡| 精品午夜av| 欧美精品羞羞答答| 天堂va欧美ⅴa亚洲va一国产| 国产精品一区二区三区美女| 精品国产欧美日韩| 91超碰国产精品| 日本电影久久久| 97视频热人人精品免费| 国产毛片一区| 国产精品久久久久77777丨| 日韩免费福利视频| 伊人国产精品| 美腿丝袜亚洲三区| 婷婷国产精品| 日本午夜精品久久久久| 伊人久久国产| 亚洲字幕久久| 91视频一区| 模特精品在线| 国产精品扒开腿做爽爽爽软件| 青青青免费在线视频| 免费观看在线色综合| 国产不卡精品在线| 一区二区三区四区在线观看国产日韩| 免费一级欧美片在线观看网站| 国户精品久久久久久久久久久不卡| 蜜臀久久99精品久久久画质超高清| 国产精品主播在线观看| 欧美成人国产| 欧美激情综合| 久久xxxx精品视频| 国产精品福利在线观看播放| 久久亚洲国产精品一区二区| 精品视频免费| 蜜桃久久av| 成人影视亚洲图片在线| 亚洲男女自偷自拍| 日韩av二区| 日韩精品国产欧美| 91精品在线观看国产| 国产乱人伦精品一区| 精品一区在线| 精品成av人一区二区三区 | 国产毛片精品| 在线一区免费观看| 捆绑调教日本一区二区三区| 综合激情在线| 日韩精品一区二区三区免费观影| 日本三级亚洲精品|