目錄
創(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當中,并沒有涉及任何的加鎖操作,因此:當多個線程同時調用put()的時候,有可能在兩個鍵的哈希值一樣的時候,之后調用put()的線程新增的值覆蓋掉最開始線程新增的值。
? 圖解:
??
?我們了解到,擴容的操作,實際上是HashMap重現初始化一個原來大小2倍的數組,并且根據新的數組的長度,重新哈希的這樣一個過程。
? 如果執(zhí)行并發(fā)擴容,那么,很有可能在擴容的時候,讓哈希表當中某一個哈希桶的鏈表變成了一個"環(huán)"。那么,也就意味著如果想獲取某一個元素,對哈希桶對應位置的鏈表進行遍歷的時候,沒有任何一個節(jié)點的next指針為null,那么將引發(fā)死循環(huán)。
? 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ū)別。
對于Hashtable來說,它解決線程安全問題的方式,比較"粗魯”。
Hashtable的缺點:?第一:
?Hashtable使用的直接是synchronized修飾核心方法的方式來加鎖的,那么,如果兩個線程同時只是讀取某一個變量的值,根據之前對線程安全問題的概述,如果線程僅僅只是對變量進行讀操作而并非寫操作,那么并不會發(fā)生線程安全問題。
?但是Hashtable即使是在多個線程同時讀取某個Entry的值的時候,也照樣會造成阻塞等待的情況,因此Hashtable的鎖的粒度是比較大的。
?第二:
?即使是put()、get()操作,發(fā)生線程安全問題的前提條件也必須是需要put()的兩個鍵的哈希值相同的情況,也就是,針對同一個哈希桶進行put()或者get()的情況。
?而Hashtable采用的是直接“一棒打死”。無論是否針對同一個哈希桶進行讀寫操作,只要多個線程同時調用一個map的put()或者get()方法,都會發(fā)生阻塞等待的情況。
??讓每一個哈希桶都是一把鎖。當新增元素發(fā)生哈希沖突,也就是散列到同一個哈希地址的時候,才會發(fā)生鎖沖突。
?這樣,就有效減少了不必要的鎖沖突,減小了鎖的粒度
觀察上面的圖:當兩個線程同時嘗試分別修改同一個哈希地址的1,2節(jié)點的時候,會產生阻塞等待的情況。
當兩個線程同時修改3,4節(jié)點的時候,不會產生阻塞等待。
也就是,只有發(fā)生了哈希沖突的時候,才會產生阻塞等待的情況?
觀察一下源碼:
?
??對于jdk1.8之前的代碼,是采用分段鎖的方式進行修改的。也就是,其中的N個哈希桶作為一把鎖,如果有線程同時針對這N個為一把鎖的哈希桶進行修改操作,會產生鎖沖突,造成阻塞等待。
由于get()方法是通過key得到對應的value的值的方法,本質上是“讀取”操作,當多個線程同時調用get()方法的時候,不存在線程安全問題。
因此,ConcurrentHashMap取消了對于get()方法加鎖的機制。
?
這里需要注意的地方是,ConcurrentHashmap當中:
? Ⅰ一個線程讀去數據,另外一個線程也同時去讀取數據,這個時候不存在線程安全問題,也不存在鎖沖突;因為ConcurrentHashMap沒有針對get方法加鎖
? Ⅱ一個線程去寫數據,另外一個線程也去寫數據,不存在線程安全問題,但是有可能存在鎖沖突;存在鎖沖突的前提是兩個線程針對同一個哈希桶進行寫操作。
? Ⅲ一個線程去讀數據,另外一個線程去寫數據,不存在線程安全問題,但是有可能產生鎖沖突。
??對于第Ⅲ點,如果產生了鎖沖突,那么就是意味著兩個線程一個對于同一哈希桶進行"讀"操作,另外一個針對哈希桶進行"寫"操作,并且都是哈希桶有存放元素,也就是需要遍歷的時候,才會產生鎖沖突。
? 如果哈希桶沒有存放元素,是null的,那么,請往下面第(3)點查看。
?ConcurrentHashMap內部充分利用了CAS,來削減了加鎖的次數。
?下面,舉幾個例子,關于ConcurrentHashMap是如何使用CAS的:
? 例子1、當讓存放元素之后,個數+1的時候,采用的是CAS的做法來保證數組當中實際存儲key的數量+1這個操作的原子性
??
其中,addCount方法內部采用的就是CAS的做法來實現個數+1的原子性的。?
? 例子2、?當對應的HashEntry數組當中,某一個位置不存放任何元素,也就是為null的時候,會采用cas的方式填充到對應的哈希地址。
?
?對于ConcurrentHashMap當中修改key的個數,本質上還是在addCount方法內部,通過CAS的方式來修改
?? ? ?回顧一下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)新互聯