HashMap死锁原因及替代方案
原文鏈接:http://blog.csdn.net/fhzaitian/article/details/51505516
------------------------------------------------------------------------
1、首先我們需要簡(jiǎn)單地了解一下HashMap數(shù)據(jù)結(jié)構(gòu)?
HashMap通常會(huì)用一個(gè)指針數(shù)組(假設(shè)為table[])來(lái)做分散所有的key,當(dāng)一個(gè)key被加入時(shí),會(huì)通過(guò)Hash算?
法通過(guò)key算出這個(gè)數(shù)組的下標(biāo)i,然后就把這個(gè)<key, value>插到table[i]中,如果有兩個(gè)不同的key被算了。?
但有時(shí)候兩個(gè)key算出的下標(biāo)會(huì)是一個(gè)i,那么就叫沖突,又叫碰撞,這樣會(huì)在table[i]上形成一個(gè)鏈表。所以?
如果鏈表過(guò)多或過(guò)長(zhǎng),查找算法則會(huì)變成低性能的鏈表遍歷,這是Hash表的缺陷。
我們都知道HashMap初始容量大小為16,一般來(lái)說(shuō),Hash表這個(gè)容器當(dāng)有數(shù)據(jù)要插入時(shí),都會(huì)檢查容量有沒有超過(guò)設(shè)定的thredhold,如果超過(guò),需要增大Hash表的尺寸,但是這樣一來(lái),整個(gè)Hash表里的元素都需要被重算一遍。這叫rehash,這個(gè)成本相當(dāng)?shù)拇蟆>唧w大家可以看看JDK源碼
2、現(xiàn)在來(lái)討論死鎖產(chǎn)生的原因?
HashMap是非線程安全,死鎖一般都是產(chǎn)生于并發(fā)情況下。我們假設(shè)有二個(gè)進(jìn)程T1、T2,HashMap容量為2,T1線程放入key A、B、C、D、E。在T1線程中A、B、C Hash值相同,于是形成一個(gè)鏈接,假設(shè)為A->C->B,而D、E Hash值不同,于是容量不足,需要新建一個(gè)更大尺寸的hash表,然后把數(shù)據(jù)從老的Hash表中?
遷移到新的Hash表中(refresh)。這時(shí)T2進(jìn)程闖進(jìn)來(lái)了,T1暫時(shí)掛起,T2進(jìn)程也準(zhǔn)備放入新的key,這時(shí)也?
發(fā)現(xiàn)容量不足,也refresh一把。refresh之后原來(lái)的鏈表結(jié)構(gòu)假設(shè)為C->A,之后T1進(jìn)程繼續(xù)執(zhí)行,鏈接結(jié)構(gòu)?
為A->C,這時(shí)就形成A.next=B,B.next=A的環(huán)形鏈表。一旦取值進(jìn)入這個(gè)環(huán)形鏈表就會(huì)陷入死循環(huán)。
3、替代方案?
使用ConcurrentHashMap進(jìn)行替代,ConcurrentHashMap是一個(gè)線程安全的Hash Table。可能有人會(huì)使用HashTable。當(dāng)然HashTable也是線程安全,但HashTable鎖定的是整個(gè)Hash表,效率相對(duì)比較低。而ConcurrentHashMap可以做到讀取數(shù)據(jù)不加鎖,并且其內(nèi)部的結(jié)構(gòu)可以讓其在進(jìn)行寫操作的時(shí)候能夠?qū)㈡i的粒度保持地盡量地小,
總結(jié)
以上是生活随笔為你收集整理的HashMap死锁原因及替代方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Docker的应用场景
- 下一篇: 剪映专业版PC端清理缓存与日志