HashMap姐刁、HashTable與ConcurrentHashMap
1芥牌、HashMap是非線程安全的,HashTable是線程安全的聂使。
2壁拉、HashMap的鍵和值都允許有null值存在,而HashTable則不行岩遗。
3扇商、因為線程安全的問題,HashMap效率比HashTable的要高宿礁。
4案铺、ConcurrentHashMap線程安全,加入segment梆靖,每個segment通過lock的方式實現(xiàn)線程安全控汉,避免整個數(shù)據(jù)的訪問加同步鎖,降低效率返吻。
一姑子、HashMap的內(nèi)部存儲結(jié)構(gòu)
Java中數(shù)據(jù)存儲方式最底層的兩種結(jié)構(gòu),一種是數(shù)組测僵,另一種就是鏈表街佑,數(shù)組的特點:連續(xù)空間,尋址迅速捍靠,但是在刪除或者添加元素的時候需要有較大幅度的移動沐旨,所以查詢速度快,增刪較慢榨婆。而鏈表正好相反磁携,由于空間不連續(xù),尋址困難良风,增刪元素只需修改指針谊迄,所以查詢慢、增刪快烟央。有沒有一種數(shù)據(jù)結(jié)構(gòu)來綜合一下數(shù)組和鏈表统诺,以便發(fā)揮他們各自的優(yōu)勢?答案是肯定的吊档!就是:哈希表篙议。哈希表具有較快(常量級)的查詢速度,及相對較快的增刪速度怠硼,所以很適合在海量數(shù)據(jù)的環(huán)境中使用鬼贱。一般實現(xiàn)哈希表的方法采用“拉鏈法”,我們可以理解為“鏈表的數(shù)組”香璃,如下圖:
從上圖中这难,我們可以發(fā)現(xiàn)哈希表是由數(shù)組+鏈表組成的,一個長度為16的數(shù)組中葡秒,每個元素存儲的是一個鏈表的頭結(jié)點姻乓。那么這些元素是按照什么樣的規(guī)則存儲到數(shù)組中呢。一般情況是通過hash(key)%len獲得眯牧,也就是元素的key的哈希值對數(shù)組長度取模得到蹋岩。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12学少。所以12剪个、28、108以及140都存儲在數(shù)組下標為12的位置版确。它的內(nèi)部其實是用一個Entity數(shù)組來實現(xiàn)的扣囊,屬性有key、value绒疗、next侵歇。接下來我會從初始化階段詳細的講解HashMap的內(nèi)部結(jié)構(gòu)。
二吓蘑、HashTable的內(nèi)部存儲結(jié)構(gòu)
HashTable和HashMap采用相同的存儲機制惕虑,二者的實現(xiàn)基本一致,不同的是:
1磨镶、HashMap是非線程安全的溃蔫,HashTable是線程安全的,內(nèi)部的方法基本都是synchronized棋嘲。
2酒唉、HashTable不允許有null值的存在。
在HashTable中調(diào)用put方法時沸移,如果key為null痪伦,直接拋出NullPointerException。其它細微的差別還有雹锣,比如初始化Entry數(shù)組的大小等等网沾,但基本思想和HashMap一樣。
通過分析Hashtable就知道蕊爵,synchronized是針對整張Hash表的辉哥,即每次鎖住整張表讓線程獨占
三、ConcurrentHashMap的內(nèi)部存儲結(jié)構(gòu)
ConcurrentHashMap允許多個修改操作并發(fā)進行,其關(guān)鍵在于使用了鎖分離技術(shù)醋旦。它使用了多個鎖來控制對hash表的不同部分進行的修改恒水。ConcurrentHashMap內(nèi)部使用段(Segment)來表示這些不同的部分,每個段其實就是一個小的hash table饲齐,它們有自己的鎖钉凌。只要多個修改操作發(fā)生在不同的段上,它們就可以并發(fā)進行捂人。
有些方法需要跨段御雕,比如size()和containsValue(),它們可能需要鎖定整個表而而不僅僅是某個段滥搭,這需要按順序鎖定所有段酸纲,操作完畢后,又按順序釋放所有段的鎖瑟匆。這里“按順序”是很重要的闽坡,否則極有可能出現(xiàn)死鎖,在ConcurrentHashMap內(nèi)部脓诡,段數(shù)組是final的无午,并且其成員變量實際上也是final的,但是祝谚,僅僅是將數(shù)組聲明為final的并不保證數(shù)組成員也是final的宪迟,這需要實現(xiàn)上的保證。這可以確保不會出現(xiàn)死鎖交惯,因為獲得鎖的順序是固定的次泽。
一、結(jié)構(gòu)解析
ConcurrentHashMap和Hashtable主要區(qū)別就是圍繞著鎖的粒度以及如何鎖,可以簡單理解成把一個大的HashTable分解成多個席爽,形成了鎖分離意荤。如圖:
而Hashtable的實現(xiàn)方式是---鎖整個hash表
二、應(yīng)用場景
當有一個大數(shù)組時需要在多個線程共享時就可以考慮是否把它給分層多個節(jié)點了只锻,避免大鎖玖像。并可以考慮通過hash算法進行一些模塊定位。
其實不止用于線程齐饮,當設(shè)計數(shù)據(jù)表的事務(wù)時(事務(wù)某種意義上也是同步機制的體現(xiàn))捐寥,可以把一個表看成一個需要同步的數(shù)組,如果操作的表數(shù)據(jù)太多時就可以考慮事務(wù)分離了(這也是為什么要避免大表的出現(xiàn))祖驱,比如把數(shù)據(jù)進行字段拆分握恳,水平分表等.