Java Map、HashMap, ConcurrentHashMap全面總結

Map

HashMap

  • 環形鏈表的問題:具體見疫苗:Java HashMap的死循環 | | 酷 殼 - CoolShell
    • 環形鏈表的形成是發生在併發rehash的過程中的
    • 並不是說rehash就直接導致死循環了,rehash只是導致的了環形鏈表的形成
  • 底層數據結構
    • 數組
    • 鏈表
    • 紅黑樹
  • 擴容
    • 時機:map中的key個數達到容量c * 負載因子loadFactor的時候
    • 過程
      • 容量擴大一倍
      • rehash一次
  • 衝突解決
    • 默認的策略是在相應的index位置形成鏈表
    • 當鏈表的數量達到8個時,出於性能考慮會把鏈表轉成紅黑樹
  • 快速失敗是什麼意思? 集合遍歷的時候檢測是否集合中的元素個數有變動,有變動就立即拋出異常ConcurrentModificationException

The number of times this HashMap has been structurally modified Structural modifications are those that change the number of mappings in the HashMap or otherwise modify its internal structure (e.g.,
rehash). This field is used to make iterators on Collection-views of
the HashMap fail-fast. (See ConcurrentModificationException).

  • loadFactor是用來幹什麼的?決定是否擴容

ConcurrentHashMap

  • 底層數據結構
    • 數組
    • 鏈表
    • 紅黑樹
  • 擴容過程
    • JDK7鎖住所有段,然後擴容,保證一致性
    • JDK8多線程擴容,標記要遷移的點(要遷移的點node的hash值會被標記成爲MOVED,即-3),當一個線程要獲取fh被標記爲MOVED的節點時,要先幫忙擴容,然後再獲取值
  • size是怎麼計算的?
    • 默認是使用baseCount,如果因爲併發導致這個屬性更新失敗,會使用counterCells來計數。
    • 計數的時候,會使用先計算兩次,如果沒變化,就認爲總數是正確
    • 如果一直在變化,就多嘗試兩次。如果還是失敗,就鎖全表進行計算。
  • JDK7和JDK8上的分段鎖機制的差別
    • DK7使用ReentrantLock來實現分段鎖
    • JDK8使用CAS+synchronized,爲什麼使用synchronized+CAS, 而不是segment+ReentrantLock呢?ConcurrentHashMap 1.8爲什麼要使用CAS+Synchronized取代Segment+ReentrantLock - 羊飛 - 博客園
      • [x]synchronized默認情況是偏向鎖,或者是自旋鎖,效率高,ReentrantLock則是tryLock之後就掛起,這樣導致有線程上下文切換的成本, JDK7版本這裏會tryLock很多次
      • [x]鎖的粒度細化到了node級別,粒度更小,併發能力更大

常見面試題

Map

  • rehash的時候,鏈表上的點還會在同一個鏈表上麼?@可能不在同一個位置上了, 這是因爲桶位置的計算方式是hash&(cap-1), hash值是不變的, cap變化的情況,可能導致計算的index不一樣, 譬如cap爲16,hash分別爲5, 21, 37, rehash之後,桶的index分別爲5, 21,

HashMap

  • 擴容過程 @見上面的筆記
  • size爲什麼是2的整數倍?
    • 計算index的時候可以減小衝突
    • 計算index的公式 hash & (size - 1)
  • 衝突解決的過程 @見上面的筆記

ConcurrentHashMap

  • 爲什麼JDK8中要用CAS+synchronized替代segment+ReentrantLock的實現來防止併發?@見上面的筆記
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章