ConcurrentHashMap基於JDK1.8源碼剖析

前言

聲明,本文用的是jdk1.8html

前面章節回顧:java

本篇主要講解ConCurrentHashMap~面試

看這篇文章以前最好是有點數據結構的基礎:算法

固然了,若是講得有錯的地方還請你們多多包涵並不吝在評論去指正~數組

1、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關鍵字修飾,導致它每次獲取的都是最新設置的值

 

 

 

 

2、總結

上面簡單介紹了ConcurrentHashMap的核心知識,還有不少知識點都沒有說起到,做者的水平也不能將其弄懂~~有興趣進入的同窗可到下面的連接繼續學習。

下面我來簡單總結一下ConcurrentHashMap的核心要點:

  • 底層結構是散列表(數組+鏈表)+紅黑樹,這一點和HashMap是同樣的。
  • Hashtable是將全部的方法進行同步,效率低下。而ConcurrentHashMap做爲一個高併發的容器,它是經過部分鎖定+CAS算法來進行實現線程安全的。CAS算法也能夠認爲是樂觀鎖的一種~
  • 在高併發環境下,統計數據(計算size...等等)實際上是無心義的,由於在下一時刻size值就變化了。
  • get方法是非阻塞,無鎖的。重寫Node類,經過volatile修飾next來實現每次獲取都是最新設置的值
  • ConcurrentHashMap的key和Value都不能爲null

參考資料:

明天要是無心外的話,可能會寫Set集合,敬請期待哦~~~~

文章的目錄導航zhongfucheng.bitcron.com/post/shou-j…

若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y。爲了你們方便,剛新建了一下qq羣:742919422,你們也能夠去交流交流。謝謝支持了!但願能多介紹給其餘有須要的朋友

相關文章
相關標籤/搜索