線程不安全的HashMap
????因?yàn)槎嗑€程環(huán)境下,使用Hashmap進(jìn)行put操作會(huì)引起死循環(huán)灿渴,導(dǎo)致CPU利用率接近100%成黄,所以在并發(fā)情況下不能使用HashMap。
效率低下的HashTable容器
?????HashTable容器使用synchronized來保證線程安全逻杖,但在線程競(jìng)爭(zhēng)激烈的情況下HashTable的效率非常低下奋岁。因?yàn)楫?dāng)一個(gè)線程訪問HashTable的同步方法時(shí),其他線程訪問HashTable的同步方法時(shí)荸百,可能會(huì)進(jìn)入阻塞或輪詢狀態(tài)闻伶。如線程1使用put進(jìn)行添加元素,線程2不但不能使用put方法添加元素够话,并且也不能使用get方法來獲取元素蓝翰,所以競(jìng)爭(zhēng)越激烈效率越低光绕。
鎖分段技術(shù)
????HashTable容器在競(jìng)爭(zhēng)激烈的并發(fā)環(huán)境下表現(xiàn)出效率低下的原因,是因?yàn)樗性L問HashTable的線程都必須競(jìng)爭(zhēng)同一把鎖畜份,那假如容器里有多把鎖诞帐,每一把鎖用于鎖容器其中一部分?jǐn)?shù)據(jù),那么當(dāng)多線程訪問容器里不同數(shù)據(jù)段的數(shù)據(jù)時(shí)爆雹,線程間就不會(huì)存在鎖競(jìng)爭(zhēng)停蕉,從而可以有效的提高并發(fā)訪問效率,這就是ConcurrentHashMap所使用的鎖分段技術(shù)钙态,首先將數(shù)據(jù)分成一段一段的存儲(chǔ)慧起,然后給每一段數(shù)據(jù)配一把鎖,當(dāng)一個(gè)線程占用鎖訪問其中一個(gè)段數(shù)據(jù)的時(shí)候册倒,其他段的數(shù)據(jù)也能被其他線程訪問蚓挤。有些方法需要跨段,比如size()和containsValue()驻子,它們可能需要鎖定整個(gè)表而而不僅僅是某個(gè)段灿意,這需要按順序鎖定所有段,操作完畢后崇呵,又按順序釋放所有段的鎖脾歧。這里“按順序”是很重要的,否則極有可能出現(xiàn)死鎖演熟,在ConcurrentHashMap內(nèi)部鞭执,段數(shù)組是final的,并且其成員變量實(shí)際上也是final的芒粹,但是兄纺,僅僅是將數(shù)組聲明為final的并不保證數(shù)組成員也是final的,這需要實(shí)現(xiàn)上的保證化漆。這可以確保不會(huì)出現(xiàn)死鎖估脆,因?yàn)楂@得鎖的順序是固定的。
應(yīng)用場(chǎng)景
????當(dāng)有一個(gè)大數(shù)組時(shí)需要在多個(gè)線程共享時(shí)就可以考慮是否把它給分層多個(gè)節(jié)點(diǎn)了座云,避免大鎖疙赠。并可以考慮通過hash算法進(jìn)行一些模塊定位。
其實(shí)不止用于線程朦拖,當(dāng)設(shè)計(jì)數(shù)據(jù)表的事務(wù)時(shí)(事務(wù)某種意義上也是同步機(jī)制的體現(xiàn))圃阳,可以把一個(gè)表看成一個(gè)需要同步的數(shù)組,如果操作的表數(shù)據(jù)太多時(shí)就可以考慮事務(wù)分離了(這也是為什么要避免大表的出現(xiàn))璧帝,比如把數(shù)據(jù)進(jìn)行字段拆分捍岳,水平分表等.
ConcurrentHashMap是由Segment數(shù)組結(jié)構(gòu)和HashEntry數(shù)組結(jié)構(gòu)組成。
Segment是一種可重入鎖ReentrantLock,在ConcurrentHashMap里扮演鎖的角色锣夹,HashEntry則用于存儲(chǔ)鍵值對(duì)數(shù)據(jù)页徐。
一個(gè)ConcurrentHashMap里包含一個(gè)Segment數(shù)組,Segment的結(jié)構(gòu)和HashMap類似银萍,是一種數(shù)組和鏈表結(jié)構(gòu)变勇, 一個(gè)Segment里包含一個(gè)HashEntry數(shù)組,每個(gè)HashEntry是一個(gè)鏈表結(jié)構(gòu)的元素贴唇, 每個(gè)Segment守護(hù)者一個(gè)HashEntry數(shù)組里的元素,當(dāng)對(duì)HashEntry數(shù)組的數(shù)據(jù)進(jìn)行修改時(shí)搀绣,必須首先獲得它對(duì)應(yīng)的Segment鎖