HashMap,Hashtable,ConcurrentHashMap-創(chuàng)新互聯

目錄

創(chuàng)新互聯成立于2013年,我們提供高端成都網站建設網站制作、網站設計、網站定制、成都營銷網站建設微信小程序開發(fā)、微信公眾號開發(fā)、成都網站推廣服務,提供專業(yè)營銷思路、內容策劃、視覺設計、程序開發(fā)來完成項目落地,為成都發(fā)電機租賃企業(yè)提供源源不斷的流量和訂單咨詢。

一、多線程使用HashMap的一些線程安全問題

①造成數據新增丟失

②擴容時候,造成鏈表成環(huán)

二、Hashtable和HashMap的區(qū)別?

①核心方法加鎖

②其他語法上面的略微差異?

三、引入ConcurrentHashMap【重要】

①ConcurrentHashMap相比于Hashtable的優(yōu)勢

Hashtable的缺點:

ConcurrentHashMap的一些優(yōu)化措施(jdk1.8往后)

(1)每一個哈希桶,都是一把"鎖"

??編輯

(2)ConcurrentHashMap不針對get(Object key)方法加鎖

(3)ConcurrentHashMap針對部分修改操作,采用了volatile+原子的方式,讓寫的操作與不加鎖的get()方式不會產生鎖沖突?

(4)擴容操作的優(yōu)化


?這三種數據結構,都是對于哈希表的具體實現。

?有關哈希表的具體設計,我們在前面的文章當中也提到了,關于HashMap的簡單源碼分析,以及HashMap的具體的一些實現,也在下面這兩篇文章當中提到了:

?(1條消息) HashMap簡單源碼分析_革凡成圣211的博客-博客https://blog.csdn.net/weixin_56738054/article/details/127786631?spm=1001.2014.3001.5501
(1條消息) Java的手寫簡單的哈希表_革凡成圣211的博客-博客https://blog.csdn.net/weixin_56738054/article/details/127416821?spm=1001.2014.3001.5501


下面,將重點分析,多線程使用HashMap的一些問題,以及Hashtable、ConcurrentHashMap和HashMap之間的一些區(qū)別。


一、多線程使用HashMap的一些線程安全問題 ? ? ?①造成數據新增丟失

? 因為HashMap當中,并沒有涉及任何的加鎖操作,因此:當多個線程同時調用put()的時候,有可能在兩個鍵的哈希值一樣的時候,之后調用put()的線程新增的值覆蓋掉最開始線程新增的值。

? 圖解:

??


②擴容時候,造成鏈表成環(huán)

?我們了解到,擴容的操作,實際上是HashMap重現初始化一個原來大小2倍的數組,并且根據新的數組的長度,重新哈希的這樣一個過程。

? 如果執(zhí)行并發(fā)擴容,那么,很有可能在擴容的時候,讓哈希表當中某一個哈希桶的鏈表變成了一個"環(huán)"。那么,也就意味著如果想獲取某一個元素,對哈希桶對應位置的鏈表進行遍歷的時候,沒有任何一個節(jié)點的next指針為null,那么將引發(fā)死循環(huán)。


二、Hashtable和HashMap的區(qū)別? ? ? ? ①核心方法加鎖

? Hashtable的核心方法put()和get()方法被加鎖了。因此,Hashtable是線程安全的

??


②其他語法上面的略微差異?

?(1)HashMap允許null值作為鍵和值,而Hashtable不允許null作為鍵和值。

?(2)添加值的哈希算法不同,對于HashMap來說,添加值的哈希算法采用的是內部自定義的一個哈希算法,而Hashtable采用的直接是key.hash()的方式計算出的哈希值。但是負載因子都是0.75.

?(3)初始化的容量不同:HashMap的默認初始容量為16,而Hashtable默認的初始容量為11.

?(4)擴容的方式不同:HashMap采用的是2倍大小的方式擴容,而Hashtable擴容的規(guī)則為當前容量*2+1.

?...這些差異,羅列一部分即可,最重要的還是多線程和單線程的使用環(huán)境區(qū)別。


三、引入ConcurrentHashMap【重要】 ①ConcurrentHashMap相比于Hashtable的優(yōu)勢

對于Hashtable來說,它解決線程安全問題的方式,比較"粗魯”。

Hashtable的缺點:

?第一:

?Hashtable使用的直接是synchronized修飾核心方法的方式來加鎖的,那么,如果兩個線程同時只是讀取某一個變量的值,根據之前對線程安全問題的概述,如果線程僅僅只是對變量進行讀操作而并非寫操作,那么并不會發(fā)生線程安全問題。

?但是Hashtable即使是在多個線程同時讀取某個Entry的值的時候,也照樣會造成阻塞等待的情況,因此Hashtable的鎖的粒度是比較大的。


?第二:

?即使是put()、get()操作,發(fā)生線程安全問題的前提條件也必須是需要put()的兩個鍵的哈希值相同的情況,也就是,針對同一個哈希桶進行put()或者get()的情況。

?而Hashtable采用的是直接“一棒打死”。無論是否針對同一個哈希桶進行讀寫操作,只要多個線程同時調用一個map的put()或者get()方法,都會發(fā)生阻塞等待的情況。


ConcurrentHashMap的一些優(yōu)化措施(jdk1.8往后) ? ? ?(1)每一個哈希桶,都是一把"鎖"

?讓每一個哈希桶都是一把鎖。當新增元素發(fā)生哈希沖突,也就是散列到同一個哈希地址的時候,才會發(fā)生鎖沖突。

?這樣,就有效減少了不必要的鎖沖突,減小了鎖的粒度

?

觀察上面的圖:當兩個線程同時嘗試分別修改同一個哈希地址的1,2節(jié)點的時候,會產生阻塞等待的情況。

當兩個線程同時修改3,4節(jié)點的時候,不會產生阻塞等待。

也就是,只有發(fā)生了哈希沖突的時候,才會產生阻塞等待的情況?

觀察一下源碼:

?


??對于jdk1.8之前的代碼,是采用分段鎖的方式進行修改的。也就是,其中的N個哈希桶作為一把鎖,如果有線程同時針對這N個為一把鎖的哈希桶進行修改操作,會產生鎖沖突,造成阻塞等待。


(2)ConcurrentHashMap不針對get(Object key)方法加鎖

由于get()方法是通過key得到對應的value的值的方法,本質上是“讀取”操作,當多個線程同時調用get()方法的時候,不存在線程安全問題。

因此,ConcurrentHashMap取消了對于get()方法加鎖的機制。

?

這里需要注意的地方是,ConcurrentHashmap當中:

? Ⅰ一個線程讀去數據,另外一個線程也同時去讀取數據,這個時候不存在線程安全問題,也不存在鎖沖突;因為ConcurrentHashMap沒有針對get方法加鎖

? Ⅱ一個線程去寫數據,另外一個線程也去寫數據,不存在線程安全問題,但是有可能存在鎖沖突;存在鎖沖突的前提是兩個線程針對同一個哈希桶進行寫操作。

? Ⅲ一個線程去讀數據,另外一個線程去寫數據,不存在線程安全問題,但是有可能產生鎖沖突。

??對于第Ⅲ點,如果產生了鎖沖突,那么就是意味著兩個線程一個對于同一哈希桶進行"讀"操作,另外一個針對哈希桶進行"寫"操作,并且都是哈希桶有存放元素,也就是需要遍歷的時候,才會產生鎖沖突。

? 如果哈希桶沒有存放元素,是null的,那么,請往下面第(3)點查看。


(3)ConcurrentHashMap針對部分修改操作,采用了volatile+原子的方式,讓寫的操作變?yōu)?原子"的,從而與不加鎖的讀操作不會產生鎖沖突?

?ConcurrentHashMap內部充分利用了CAS,來削減了加鎖的次數。

?下面,舉幾個例子,關于ConcurrentHashMap是如何使用CAS的:

? 例子1、當讓存放元素之后,個數+1的時候,采用的是CAS的做法來保證數組當中實際存儲key的數量+1這個操作的原子性

??

其中,addCount方法內部采用的就是CAS的做法來實現個數+1的原子性的。?


? 例子2、?當對應的HashEntry數組當中,某一個位置不存放任何元素,也就是為null的時候,會采用cas的方式填充到對應的哈希地址。

?

?對于ConcurrentHashMap當中修改key的個數,本質上還是在addCount方法內部,通過CAS的方式來修改


(4)擴容操作的優(yōu)化

?? ? ?回顧一下HashMap或者Hashtable擴容的操作,它們都是創(chuàng)建一個更大容量的數組,然后把每一個元素重新哈希的做法。

? 在數據量比較大的時候,會造成可能某次put()之后,線程會阻塞等待很長的時間,才可以完成擴容。

?擴容條件:

?①當存放Node節(jié)點的數組長度小于64,并且單個哈希桶的鏈表的存放節(jié)點個數達到8的時候,會觸發(fā)擴容;如果存放節(jié)點的數組長度>=64,那么會把當前的鏈表樹化為紅黑樹。

?②當存儲的實際key的數量/Node數組的長度達到負載因子的時候,會觸發(fā)擴容


以上兩點,和jdk1.8版本的HashMap的擴容前提條件類似,沒有太大的差別。

滿足上面的條件之后,會進入到下面的操作:

首先申請一個原來數組大小2倍的新數組

如果有多個線程同時嘗試擴容,那么,ConcurrentHashMap會對這些線程進行"分工”。何為分工呢?畫個圖簡單看一看:

?也就是,每一個線程,分別對原有的數組上面的元素分別進行"搬運"操作,讓它們都被各自的線程重新哈希到新的數組上面的位置,這樣的效率會更加的高。


? 同時,如果ConcurrentHashMap如果正在擴容的時候:

? 其他線程調用get方法,那么調用的線程會查詢舊數組和新的數組當中是否存在對應的key。

? 其他線程如果調用remove方法,那么調用的線程會把舊數組和新的數組當中的key都刪除掉。

? 而不是一直阻塞等待,直到擴容執(zhí)行完畢。


??

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧

文章名稱:HashMap,Hashtable,ConcurrentHashMap-創(chuàng)新互聯
URL分享:http://muchs.cn/article18/eigdp.html

成都網站建設公司_創(chuàng)新互聯,為您提供用戶體驗域名注冊、網站內鏈、電子商務網站維護、網站設計

廣告

聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯

成都定制網站網頁設計