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