目錄
一、多線程使用HashMap的一些線程安全問題
①造成數(shù)據(jù)新增丟失
②擴容時候,造成鏈表成環(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()方式不會產(chǎn)生鎖沖突?
(4)擴容操作的優(yōu)化
?這三種數(shù)據(jù)結(jié)構(gòu),都是對于哈希表的具體實現(xiàn)。
?有關(guān)哈希表的具體設(shè)計,我們在前面的文章當中也提到了,關(guān)于HashMap的簡單源碼分析,以及HashMap的具體的一些實現(xiàn),也在下面這兩篇文章當中提到了:
?(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當中,并沒有涉及任何的加鎖操作,因此:當多個線程同時調(diào)用put()的時候,有可能在兩個鍵的哈希值一樣的時候,之后調(diào)用put()的線程新增的值覆蓋掉最開始線程新增的值。
? 圖解:
??
?我們了解到,擴容的操作,實際上是HashMap重現(xiàn)初始化一個原來大小2倍的數(shù)組,并且根據(jù)新的數(shù)組的長度,重新哈希的這樣一個過程。
? 如果執(zhí)行并發(fā)擴容,那么,很有可能在擴容的時候,讓哈希表當中某一個哈希桶的鏈表變成了一個"環(huán)"。那么,也就意味著如果想獲取某一個元素,對哈希桶對應(yīng)位置的鏈表進行遍歷的時候,沒有任何一個節(jié)點的next指針為null,那么將引發(fā)死循環(huán)。
? Hashtable的核心方法put()和get()方法被加鎖了。因此,Hashtable是線程安全的
??
?(1)HashMap允許null值作為鍵和值,而Hashtable不允許null作為鍵和值。
?(2)添加值的哈希算法不同,對于HashMap來說,添加值的哈希算法采用的是內(nèi)部自定義的一個哈希算法,而Hashtable采用的直接是key.hash()的方式計算出的哈希值。但是負載因子都是0.75.
?(3)初始化的容量不同:HashMap的默認初始容量為16,而Hashtable默認的初始容量為11.
?(4)擴容的方式不同:HashMap采用的是2倍大小的方式擴容,而Hashtable擴容的規(guī)則為當前容量*2+1.
?...這些差異,羅列一部分即可,最重要的還是多線程和單線程的使用環(huán)境區(qū)別。
對于Hashtable來說,它解決線程安全問題的方式,比較"粗魯”。
Hashtable的缺點:?第一:
?Hashtable使用的直接是synchronized修飾核心方法的方式來加鎖的,那么,如果兩個線程同時只是讀取某一個變量的值,根據(jù)之前對線程安全問題的概述,如果線程僅僅只是對變量進行讀操作而并非寫操作,那么并不會發(fā)生線程安全問題。
?但是Hashtable即使是在多個線程同時讀取某個Entry的值的時候,也照樣會造成阻塞等待的情況,因此Hashtable的鎖的粒度是比較大的。
?第二:
?即使是put()、get()操作,發(fā)生線程安全問題的前提條件也必須是需要put()的兩個鍵的哈希值相同的情況,也就是,針對同一個哈希桶進行put()或者get()的情況。
?而Hashtable采用的是直接“一棒打死”。無論是否針對同一個哈希桶進行讀寫操作,只要多個線程同時調(diào)用一個map的put()或者get()方法,都會發(fā)生阻塞等待的情況。
??讓每一個哈希桶都是一把鎖。當新增元素發(fā)生哈希沖突,也就是散列到同一個哈希地址的時候,才會發(fā)生鎖沖突。
?這樣,就有效減少了不必要的鎖沖突,減小了鎖的粒度
觀察上面的圖:當兩個線程同時嘗試分別修改同一個哈希地址的1,2節(jié)點的時候,會產(chǎn)生阻塞等待的情況。
當兩個線程同時修改3,4節(jié)點的時候,不會產(chǎn)生阻塞等待。
也就是,只有發(fā)生了哈希沖突的時候,才會產(chǎn)生阻塞等待的情況?
觀察一下源碼:
?
??對于jdk1.8之前的代碼,是采用分段鎖的方式進行修改的。也就是,其中的N個哈希桶作為一把鎖,如果有線程同時針對這N個為一把鎖的哈希桶進行修改操作,會產(chǎn)生鎖沖突,造成阻塞等待。
由于get()方法是通過key得到對應(yīng)的value的值的方法,本質(zhì)上是“讀取”操作,當多個線程同時調(diào)用get()方法的時候,不存在線程安全問題。
因此,ConcurrentHashMap取消了對于get()方法加鎖的機制。
?
這里需要注意的地方是,ConcurrentHashmap當中:
? Ⅰ一個線程讀去數(shù)據(jù),另外一個線程也同時去讀取數(shù)據(jù),這個時候不存在線程安全問題,也不存在鎖沖突;因為ConcurrentHashMap沒有針對get方法加鎖
? Ⅱ一個線程去寫數(shù)據(jù),另外一個線程也去寫數(shù)據(jù),不存在線程安全問題,但是有可能存在鎖沖突;存在鎖沖突的前提是兩個線程針對同一個哈希桶進行寫操作。
? Ⅲ一個線程去讀數(shù)據(jù),另外一個線程去寫數(shù)據(jù),不存在線程安全問題,但是有可能產(chǎn)生鎖沖突。
??對于第Ⅲ點,如果產(chǎn)生了鎖沖突,那么就是意味著兩個線程一個對于同一哈希桶進行"讀"操作,另外一個針對哈希桶進行"寫"操作,并且都是哈希桶有存放元素,也就是需要遍歷的時候,才會產(chǎn)生鎖沖突。
? 如果哈希桶沒有存放元素,是null的,那么,請往下面第(3)點查看。
?ConcurrentHashMap內(nèi)部充分利用了CAS,來削減了加鎖的次數(shù)。
?下面,舉幾個例子,關(guān)于ConcurrentHashMap是如何使用CAS的:
? 例子1、當讓存放元素之后,個數(shù)+1的時候,采用的是CAS的做法來保證數(shù)組當中實際存儲key的數(shù)量+1這個操作的原子性
??
其中,addCount方法內(nèi)部采用的就是CAS的做法來實現(xiàn)個數(shù)+1的原子性的。?
? 例子2、?當對應(yīng)的HashEntry數(shù)組當中,某一個位置不存放任何元素,也就是為null的時候,會采用cas的方式填充到對應(yīng)的哈希地址。
?
?對于ConcurrentHashMap當中修改key的個數(shù),本質(zhì)上還是在addCount方法內(nèi)部,通過CAS的方式來修改
?? ? ?回顧一下HashMap或者Hashtable擴容的操作,它們都是創(chuàng)建一個更大容量的數(shù)組,然后把每一個元素重新哈希的做法。
? 在數(shù)據(jù)量比較大的時候,會造成可能某次put()之后,線程會阻塞等待很長的時間,才可以完成擴容。
?擴容條件:
?①當存放Node
節(jié)點的數(shù)組長度小于64,并且單個哈希桶的鏈表的存放節(jié)點個數(shù)達到8的時候,會觸發(fā)擴容;如果存放節(jié)點的數(shù)組長度>=64,那么會把當前的鏈表樹化為紅黑樹。 ?②當存儲的實際key的數(shù)量/Node
數(shù)組的長度達到負載因子的時候,會觸發(fā)擴容
以上兩點,和jdk1.8版本的HashMap的擴容前提條件類似,沒有太大的差別。
滿足上面的條件之后,會進入到下面的操作:
首先申請一個原來數(shù)組大小2倍的新數(shù)組
如果有多個線程同時嘗試擴容,那么,ConcurrentHashMap會對這些線程進行"分工”。何為分工呢?畫個圖簡單看一看:
?也就是,每一個線程,分別對原有的數(shù)組上面的元素分別進行"搬運"操作,讓它們都被各自的線程重新哈希到新的數(shù)組上面的位置,這樣的效率會更加的高。
? 同時,如果ConcurrentHashMap如果正在擴容的時候:
? 其他線程調(diào)用get方法,那么調(diào)用的線程會查詢舊數(shù)組和新的數(shù)組當中是否存在對應(yīng)的key。
? 其他線程如果調(diào)用remove方法,那么調(diào)用的線程會把舊數(shù)組和新的數(shù)組當中的key都刪除掉。
? 而不是一直阻塞等待,直到擴容執(zhí)行完畢。
??
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧
文章名稱:HashMap,Hashtable,ConcurrentHashMap-創(chuàng)新互聯(lián)
URL分享:http://jinyejixie.com/article18/eigdp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗、域名注冊、網(wǎng)站內(nèi)鏈、電子商務(wù)、網(wǎng)站維護、網(wǎng)站設(shè)計
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)