【轉(zhuǎn)載 http://www.bootdo.com/blog/open/post/130 】
相信每個JAVA程序員都了解HashMap催烘,最大的問題是線程不安全衙荐,因為方法中不涉及到同步矾屯,也正因為如此敏沉,HashMap的效率非常高耻矮,在不涉及線程安全的程序中廣泛被應(yīng)用秦躯。然而當(dāng)涉及到多線程作業(yè)時,就會出現(xiàn)一些問題裆装。為了解決這些問題JAVA提供了Hashtable踱承,這是一種整體加鎖的數(shù)據(jù)結(jié)構(gòu),然而效率不敢恭維哨免。這時候就有了ConcurrentHashMap茎活。
一個例子說明三者關(guān)系:
前提:某個衛(wèi)生間共有16個隔間。
HashMap:每個隔間都沒鎖門琢唾,有人想上廁所妙色,管理員指給他一個隔間,里面沒人的話正常用慧耍,里面有人的話把這個人趕出來然后用身辨。
優(yōu)點,每個人進來不耽誤都能用芍碧;缺點煌珊,每一個上廁所的人都有被中途趕出來的危險。
Hashtable:在衛(wèi)生間外面安裝一個大門泌豆,有人想上廁所定庵,問管理員要一個鑰匙進門,把門反鎖用踪危,用完后出來蔬浙,把鑰匙交換給管理員。在這個人上廁所期間贞远,其他所有人都必須在外面排號畴博。
優(yōu)點,每個人都能安心上完廁所蓝仲;缺點俱病,衛(wèi)生間外面可能已經(jīng)出了人命官疲。 =_=
ConcurrentHashMap:在衛(wèi)生間每個隔間安裝門鎖,有人想上廁所亮隙,管理員指給他一個隔間途凫,進來后這個隔間如果沒人在用則直接用,如果有人正在用溢吻,則排號维费。在這期間其他人會按規(guī)則分到不同的隔間,重復(fù)上述行為促王。
優(yōu)點:每個人都能安心上廁所犀盟,外面排隊的也被均勻分?jǐn)偂H秉c:硼砰。且蓬。。
ConcurrentHashMap實現(xiàn)的原理
ConcurrentHashMap把Map分成了N個Segment(默認16)题翰,其中Segment是線程同步的恶阴,相當(dāng)于分成了N個Hashtable。當(dāng)實現(xiàn)Put方法時豹障,在key值經(jīng)過正常的hash后冯事,還要再經(jīng)過一次segmentForHash算法,用來分配具體防盜哪個Segment血公。后來的線程如果經(jīng)過計算也是放在這個Segment下昵仅,則需要先獲取鎖,如果計算得出應(yīng)該放在其他的Segment累魔,則正常執(zhí)行摔笤,不會影響效率,以此實現(xiàn)線程安全垦写。ConcurrentHashMap使用鎖分離技術(shù)吕世,只要多個修改操作不發(fā)生在同一個Segment上,它們就可以并發(fā)進行梯投。
有些方法需要跨段命辖,比如size()和containsValue(),需要鎖定整個表而而不僅僅是某個段分蓖,這需要按順序鎖定所有段尔艇,操作完畢后,又按順序釋放所有段的鎖么鹤。這里“按順序”是很重要的终娃,否則極有可能出現(xiàn)死鎖,在ConcurrentHashMap內(nèi)部午磁,段數(shù)組是final的尝抖,并且其成員變量實際上也是final的毡们,但是迅皇,僅僅是將數(shù)組聲明為final的并不保證數(shù)組成員也是final的昧辽,這需要實現(xiàn)上的保證。這可以確保不會出現(xiàn)死鎖登颓,因為獲得鎖的順序是固定的搅荞。