這裏在知乎上看到的,分享出了本身面試阿里Java
崗的面試題。html
看了一下,除了Spring
以外的其餘不少題都不會,可是不能拿學校沒講Java
做爲藉口,由於可能講了也不會。面試
可是第九個問題,我以爲應該馬上話時間研究研究了,由於以前在緩存中用到了這個。數據庫
當時也不明白具體HashMap
和ConcurrentHashMap
究竟有什麼區別。數組
只是記得以前看過有關大數據的場景下利用緩存減輕數據庫壓力的文章,文中說經常使用ConcurrentHashMap
,因此這裏緩存就用這個了,其實並不懂原理,下面,讓咱們一塊兒來研究一下。緩存
Map
你們都熟悉了,Java
中也有,JavaScript
中也有。安全
Map
是一種鍵值對類型的數據結構,根據鍵映射到值。數據結構
不分析源碼了,就把思想給你們講一下,如下主要以圖爲主。多線程
HashMap
的本質是一個可變長度的數組,在數組中每一個位置保存的是一個Entry
節點,該節點存儲有hash
、key
、value
、next
等信息。併發
Java7
中的HashMap
實現與咱們在數據結構中學習的相似,對key
進行hash
,若是衝突了,則添加到鏈表中。性能
而後查詢的時候就先根據hash
找到相應的位置,而後根據鏈表逐一比較,返回相應的value
。時間複雜度取決於鏈表的長度,時間複雜度爲O(N)
。
Java8
中對HashMap
進行了優化,若是鏈表中元素超過8
個時,就將鏈表轉化爲紅黑樹,以減小查詢的複雜度,將時間複雜度下降爲O(logN)
。
HashMap
沒有對多線程的場景下作任何的處理,不用說別的,就兩個線程同時put
,而後衝突了,二者須要操做一個鏈表/紅黑樹,這確定就會有錯誤發生,因此HashMap
是線程不安全的。
HashTable
與Java7
中的HashMap
相似,也是一個數組加鏈表,不過這個線程安全。
HashTable
線程安全,可是它的線程安全是依賴將全部修改HashTable
的代碼塊都用synchronized
修飾。
synchronized
關鍵字咱們以前在單例模式中用到過,它修飾的代碼塊,同一時刻只容許一個線程訪問,其餘線程會被阻塞,等待該線程執行完再執行。
因此,在HashTable
中,一個線程在put
,其他的線程在get
的時候就會被阻塞,沒法並行。因此不推薦使用HashTable
,雖然它線程安全。
HashMap
線程不安全,HashTable
性能又很差,固然須要設計一個新類去解決這些問題,這就是ConcurrentHashMap
。
這是Java7
中實現線程安全的思路,ConcurrentHashMap
由16
個segment
組成,每一個segment
就至關於一個HashMap
(數組+鏈表)。
segment
最多16
個,想要擴容,就是擴充每一個segment
中數組的長度。
而後只要實現每一個segment
是線程安全的,就讓這個Map
線程安全了。每一個segment
是加鎖的,對修改segment
的操做加鎖,不影響其餘segment
的使用,因此理想狀況下,最多支持16
個線程併發修改segment
,這16
個線程分別訪問不一樣的segment
。
同時,在segment
加鎖時,全部讀線程是不會受到阻塞的。
這樣設計,put
與get
的基本操做就是先找segment
,再找segment
中的數組位置,再查鏈表。
後來人們發現Java7
這樣設計太複雜了,迴歸本質。
HashMap
線程不安全的問題徹底都是出在對鏈表/紅黑樹的操做上,爲何非要建一個segment
加鎖呢?直接對鏈表/紅黑樹加鎖不就行了?
因此Java8
的ConcurrentHashMap
徹底就是HashMap
進行加鎖,實現線程安全。
這裏總結的很簡單,其實源碼中對這個的實現特別複雜,有興趣的能夠去看看,反正我是看着頭大。
HashMap
線程不安全。HashTable
線程安全,但性能差,不推薦使用。ConcurrentHashMap
線程安全。ConcurrentHashMap
在Java7
中使用segment
實現,對每一個segment
加鎖。ConcurrentHashMap
在Java8
中是直接在HashMap
的基礎上進行加鎖。參考文獻:
Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析
ConcurrentHashMap、HashTable、HashMap三兄弟