Java中的ConcurrentHashMap原理分析節選
集合是編程中最常用的數據結構。而談到並發,幾乎總是離不開集合這類高級數據結構的支持。比如兩個線程需要同時訪問一個中間臨界區Queue,比如常會用緩存作為外部文件的副本HashMap。這篇文章主要分析jdk1.5的3種並發集合類型concurrent,copyonright,queue中的ConcurrentHashMap,讓我們從原理上細致的了解它們,能夠讓我們在深度項目開發中獲益非淺。在tiger之前我們使用得最多的數據結構之一就是HashMap和Hashtable。大HashMap中未進行同步考慮,而Hashtable則使用了synchronized,帶來的直接影響就是可選擇,我們可以在單線程時使用HashMap提高效率,而多線程時用Hashtable來保證安全。
當享受著jdk帶來的便利時同樣承受它帶來的不幸惡果。通過分析Hashtable就知道,synchronized是針對整張Hash表的,即每次鎖住整張表讓線程獨占,安全的背後是巨大的浪費,慧眼獨具的Doug Lee立馬拿出了解決方案----ConcurrentHashMap。
ConcurrentHashMap和Hashtable主要區別就是圍繞著鎖的粒度以及如何鎖。

左邊便是Hashtable的實現方式---鎖整個hash表;而右邊則是ConcurrentHashMap的實現方式---鎖桶(或段)。ConcurrentHashMap將hash表分為16個桶(默認值),諸如get,put,remove等常用操作隻鎖當前需要用到的桶。試想,原來隻能一個線程進入,現在卻能同時16個寫線程進入(寫線程才需要鎖定,而讀線程幾乎不受限製,之後會提到),並發性的提升是顯而易見的。
更令人驚訝的是ConcurrentHashMap的讀取並發,因為在讀取的大多數時候都沒有用到鎖定,所以讀取操作幾乎是完全的並發操作,而寫操作鎖定的粒度又非常細,比起之前又更加快速(這一點在桶更多時表現得更明顯些)。隻有在求size等操作時才需要鎖定整個表。而在迭代時,ConcurrentHashMap使用了不同於傳統集合的快速失敗迭代器(見之前的文章《JAVA API備忘---集合》)的另一種迭代方式,我們稱為弱一致迭代器。在這種迭代方式中,當iterator被創建後集合再發生改變就不再是拋出ConcurrentModificationException,取而代之的是在改變時new新的數據從而不影響原有的數據,iterator完成後再將頭指針替換為新的數據,這樣iterator線程可以使用原來老的數據,而寫線程也可以並發的完成改變,更重要的,這保證了多個線程並發執行的連續性和擴展性,是性能提升的關鍵。
原帖地址:https://blog.csdn.net/liuzhengkang/article/details/2916620
最後更新:2017-04-03 18:52:06