第一:java底層 hashmap擴容怎么實現(xiàn)
答:可是當(dāng)哈希表接近裝滿時,因為數(shù)組的擴容問題,性能較低(轉(zhuǎn)移到更大的哈希表中).
Java默認的散列單元大小全部都是2的冪,初始值為16(2的4次冪)。假如16條鏈表中的75%鏈接有數(shù)據(jù)的時候,則認為加載因子達到默認的0.75。HahSet開始重新散列,也就是將原來的散列結(jié)構(gòu)全部拋棄,重新開辟一個散列單元大小為32(2的5次冪)的散列結(jié)果,并重新計算各個數(shù)據(jù)的存儲位置。以此類推下去.....
負載(加載)因子:0.75.-->hash表提供的空間是16 也就是說當(dāng)?shù)竭_12的時候就擴容
第二:hashtable和currenthashmap的原理
答:HashTable容器使用synchronized來保證線程安全,但在線程競爭激烈的情況下,HashTable的效率非常低下。因為當(dāng)一個線程訪問HashTable的同步方法時,其他線程訪問HashTable的同步方法時,可能會進入阻塞或輪詢狀態(tài)。如線程1使用put進行添加元素,線程2不但不能使用put方法添加元素,并且也不能使用get方法來獲取元素,所以競爭越激烈效率越低。
hashtable實現(xiàn):
底層數(shù)組+鏈表實現(xiàn),無論key還是value都**不能為null**,線程**安全**,實現(xiàn)線程安全的方式是在修改數(shù)據(jù)時鎖住整個HashTable,效率低,ConcurrentHashMap做了相關(guān)優(yōu)化
初始size為11,擴容:newsize = olesize*2+1
計算index的方法:index = (hash & 0x7FFFFFFF) % tab.length
Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴展性更好。
concurrentHashMap的原理:
底層采用分段的數(shù)組+鏈表實現(xiàn),線程**安全**
通過把整個Map分為N個Segment,可以提供相同的線程安全,但是效率提升N倍,默認提升16倍。(讀操作不加鎖,由于HashEntry的value變量是 volatile的,也能保證讀取到最新的值。)
Hashtable的synchronized是針對整張Hash表的,即每次鎖住整張表讓線程獨占,ConcurrentHashMap允許多個修改操作并發(fā)進行,其關(guān)鍵在于使用了鎖分離技術(shù)
有些方法需要跨段,比如size()和containsValue(),它們可能需要鎖定整個表而而不僅僅是某個段,這需要按順序鎖定所有段,操作完畢后,又按順序釋放所有段的鎖
擴容:段內(nèi)擴容(段內(nèi)元素超過該段對應(yīng)Entry數(shù)組長度的75%觸發(fā)擴容,不會對整個Map進行擴容),插入前檢測需不需要擴容,有效避免無效擴容
concurrentMap和hashtable比較:
ConcurrentHashMap是使用了鎖分段技術(shù)來保證線程安全的。
鎖分段技術(shù):首先將數(shù)據(jù)分成一段一段的存儲,然后給每一段數(shù)據(jù)配一把鎖,當(dāng)一個線程占用鎖訪問其中一個段數(shù)據(jù)的時候,其他段的數(shù)據(jù)也能被其他線程訪問。
ConcurrentHashMap提供了與Hashtable和SynchronizedMap不同的鎖機制。Hashtable中采用的鎖機制是一次鎖住整個hash表,從而在同一時刻只能由一個線程對其進行操作;而ConcurrentHashMap中則是一次鎖住一個桶。
ConcurrentHashMap默認將hash表分為16個桶,諸如get、put、remove等常用操作只鎖住當(dāng)前需要用到的桶。這樣,原來只能一個線程進入,現(xiàn)在卻能同時有16個寫線程執(zhí)行,并發(fā)性能的提升是顯而易見的。