前言
宣告,本文用的是jdk1.8
前面章節回顧:
- Collection總覽
- List集合就這麼簡單【原始碼剖析】
- Map集合、雜湊表、紅黑樹介紹
- HashMap就是這麼簡單【原始碼剖析】
- LinkedHashMap就這麼簡單【原始碼剖析】
- TreeMap就這麼簡單【原始碼剖析】
本篇主要講解ConCurrentHashMap~
看這篇文章之前最好是有點資料結構的基礎:
當然了,如果講得有錯的地方還請大家多多包涵並不吝在評論去指正~
一、ConCurrentHashMap剖析
ConCurrentHashMap在初學的時候反正我是沒有接觸過的,不知道你們接觸過了沒有~
這個類聽得也挺少的,在集合中是比較複雜的一個類了,它涉及到了一些多執行緒的知識點。
不瞭解或忘記多執行緒知識點的同學也不要怕,哪兒用到了多執行緒的知識點,我都會簡單介紹一下,並給出對應的資料去閱讀的~
好了,我們就來開始吧~
1.1初識ConCurrentHashMap
ConCurrentHashMap的底層是:雜湊表+紅黑樹,與HashMap是一樣的。
從前面的章節我們也可以發現:最快了解一下類是幹嘛的,我們看原始碼的頂部註釋就可以了!
我簡單翻譯了一下頂部的註釋(我英文水平渣,如果有錯的地方請多多包涵~歡迎在評論區下指正)
根據上面註釋我們可以簡單總結:
- JDK1.8底層是雜湊表+紅黑樹
- ConCurrentHashMap支援高併發的訪問和更新,它是執行緒安全的
- 檢索操作不用加鎖,get方法是非阻塞的
- key和value都不允許為null
1.2JDK1.7底層實現
上面指明的是JDK1.8底層是:雜湊表+紅黑樹,也就意味著,JDK1.7的底層跟JDK1.8是不同的~
JDK1.7的底層是:segments+HashEntry陣列:
圖來源:https://blog.csdn.net/panweiwei1994/article/details/78897275
- Segment繼承了ReentrantLock,每個片段都有了一個鎖,叫做“鎖分段”
大概瞭解一下即可~
1.3有了Hashtable為啥需要ConCurrentHashMap
- Hashtable是在每個方法上都加上了Synchronized完成同步,效率低下。
- ConcurrentHashMap通過在部分加鎖和利用CAS演算法來實現同步。
1.4CAS演算法和volatile簡單介紹
在看ConCurrentHashMap原始碼之前,我們來簡單講講CAS演算法和volatile關鍵字
CAS(比較與交換,Compare and swap) 是一種有名的無鎖演算法
CAS有3個運算元
- 記憶體值V
- 舊的預期值A
- 要修改的新值B
當且僅當預期值A和記憶體值V相同時,將記憶體值V修改為B,否則什麼都不做
- 當多個執行緒嘗試使用CAS同時更新同一個變數時,只有其中一個執行緒能更新變數的值(A和記憶體值V相同時,將記憶體值V修改為B),而其它執行緒都失敗,失敗的執行緒並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試**(否則什麼都不做)**
看了上面的描述應該就很容易理解了,先比較是否相等,如果相等則替換(CAS演算法)
接下來我們看看volatile關鍵字,在初學的時候也很少使用到volatile這個關鍵字。反正我沒用到,而又經常在看Java相關面試題的時候看到它,覺得是一個挺神祕又很難的一個關鍵字。其實不然,還是挺容易理解的~
volatile經典總結:volatile僅僅用來保證該變數對所有執行緒的可見性,但不保證原子性
我們將其拆開來解釋一下:
- 保證該變數對所有執行緒的可見性
- 在多執行緒的環境下:當這個變數修改時,所有的執行緒都會知道該變數被修改了,也就是所謂的“可見性”
- 不保證原子性
- 修改變數(賦值)實質上是在JVM中分了好幾步,而在這幾步內(從裝載變數到修改),它是不安全的。
如果沒看懂或者想要深入瞭解其原理和可參考下列博文:
1.5ConCurrentHashMap域
域物件有這麼幾個:
我們來簡單看一下他們是什麼東東:
初次閱讀完之後,有的屬性我也不太清楚它是幹什麼的,在繼續閱讀之後可能就明朗了~
1.6ConCurrentHashMap構造方法
ConcurrentHashMap的構造方法有5個:
具體的實現是這樣子的:
可以發現,在構造方法中有幾處都呼叫了tableSizeFor()
,我們來看一下他是幹什麼的:
點進去之後發現,啊,原來我看過這個方法,在HashMap的時候…..
它就是用來獲取大於引數且最接近2的整次冪的數…
賦值給sizeCtl屬性也就說明了:這是下次擴容的大小~
1.7put方法
終於來到了最核心的方法之一:put方法啦~~~~
我們先來整體看一下put方法幹了什麼事:
接下來,我們來看看初始化雜湊表的時候幹了什麼事:initTable()
- 只讓一個執行緒對雜湊表進行初始化!
1.8get方法
從頂部註釋我們可以讀到,get方法是不用加鎖的,是非阻塞的。
我們可以發現,Node節點是重寫的,設定了volatile關鍵字修飾,致使它每次獲取的都是最新設定的值
二、總結
上面簡單介紹了ConcurrentHashMap的核心知識,還有很多知識點都沒有提及到,作者的水平也不能將其弄懂~~有興趣進入的同學可到下面的連結繼續學習。
下面我來簡單總結一下ConcurrentHashMap的核心要點:
- 底層結構是雜湊表(陣列+連結串列)+紅黑樹,這一點和HashMap是一樣的。
- Hashtable是將所有的方法進行同步,效率低下。而ConcurrentHashMap作為一個高併發的容器,它是通過部分鎖定+CAS演算法來進行實現執行緒安全的。CAS演算法也可以認為是樂觀鎖的一種~
- 在高併發環境下,統計資料(計算size…等等)其實是無意義的,因為在下一時刻size值就變化了。
- get方法是非阻塞,無鎖的。重寫Node類,通過volatile修飾next來實現每次獲取都是最新設定的值
- ConcurrentHashMap的key和Value都不能為null
參考資料:
- blog.csdn.net/u010723709/…
- blog.csdn.net/melod_bc/ar…
- blog.csdn.net/panweiwei19…
- www.jianshu.com/p/e694f1e86…
明天要是無意外的話,可能會寫Set集合,敬請期待哦~~~~
文章的目錄導航:zhongfucheng.bitcron.com/post/shou-j…
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y。