ConcurrentHashMap和Hashtable的區別

yn6e 9年前發布 | 19K 次閱讀 Java開發 ConcurrentHashMap

相同點: Hashtable 和 ConcurrentHashMap都是線程安全的,可以在多線程環境中運行; key跟value都不能是null

區別: 兩者主要是性能上的差異,Hashtable的所有操作都會鎖住整個對象,雖然能夠保證線程安全,但是性能較差; ConcurrentHashMap內部使用Segment數組,每個Segment類似于Hashtable,在“寫”線程或者部分特殊的“讀”線程中鎖住的是某個Segment對象,其它的線程能夠并發執行其它的Segment對象。



Hashtable 和ConcurrentHashMap有什么分別呢?它們都可以用于多線程的環境,但是當Hashtable的大小增加到一定的時候,性能會急劇下降,因為迭代時需要被鎖定很長的時間。因為ConcurrentHashMap引入了分割(segmentation),不論它變得多么大,僅僅需要鎖定map 的某個部分,而其它的線程不需要等到迭代完成才能訪問map。簡而言之,在迭代的過程中,ConcurrentHashMap僅僅鎖定map的某個部分,而Hashtable則會鎖定整個map。


ConcurrentHashMap鎖的方式是稍微細粒度的。 ConcurrentHashMap將hash表分為16個桶(默認值),諸如get,put,remove等常用操作只鎖當前需要用到的桶。

試想,原來 只能一個線程進入,現在卻能同時16個寫線程進入(寫線程才需要鎖定,而讀線程幾乎不受限制,之后會提到),并發性的提升是顯而易見的。


ConcurrentHashMap的讀取并發,因為在讀取的大多數時候都沒有用到鎖定,所以讀取操作幾乎是完全的并發操作,


寫操作鎖定的粒度又非常細,比起之前又更加快速(這一點在桶更多時表現得更明顯些)。只有在求size等操作時才需要鎖定整個表。


迭代時,ConcurrentHashMap使用了不同于傳統集合的快速失敗迭代器的另一種迭代方式,我們稱為弱一致迭代器。在這種迭代方式中,當iterator被創建后集合再發生改變就不再是拋出 ConcurrentModificationException,取而代之的是在改變時new新的數據從而不影響原有的數 據,iterator完成后再將頭指針替換為新的數據,這樣iterator線程可以使用原來老的數據,而寫線程也可以并發的完成改變,更重要的,這保證了多個線程并發執行的連續性和擴展性,是性能提升的關鍵。

 Hashtable 的任何操作都會把整個表鎖住,是阻塞的。好處是總能獲取最實時的更新,比如說線程A調用putAll寫入大量數據,期間線程B調用get,線程B就會被阻塞,直到線程A完成putAll,因此線程B肯定能獲取到線程A寫入的完整數據。壞處是所有調用都要排隊,效率較低。
     ConcurrentHashMap 是設計為非阻塞的。在更新時會局部鎖住某部分數據,但不會把整個表都鎖住。同步讀取操作則是完全非阻塞的。好處是在保證合理的同步前提下,效率很高。壞處是嚴格來說讀取操作不能保證反映最近的更新。例如線程A調用putAll寫入大量數據,期間線程B調用get,則只能get到目前為止已經順利插入的部分數據。此外,使用默認構造器創建的ConcurrentHashMap比較占內存,如果程序需要創建巨量ConcurrentHashMap,應該在構造 時指定concurrencyLevel

 本文由用戶 yn6e 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!