總結(jié):
分段機(jī)制:segment,每段加reentrantLock可重入鎖
定位元素:1 找segment數(shù)組下標(biāo) 2 找segment的HashEntry數(shù)組下標(biāo)
get方法:不需要加鎖,value值使用了volatile關(guān)鍵字修飾
put方法:hash計(jì)算段---鎖定段---hash計(jì)算HashEntry數(shù)組---若超多閾值---擴(kuò)容---釋放;put過程中會(huì)modCount+1,為了后續(xù)的計(jì)算大小
size()方法:為了求Map的大小,需要全局鎖,但是性能差;于是采用modCount計(jì)算器,用于記錄段的大??;
ConcurrentHashMap的弱一致性:clear時(shí),是不加鎖的,所以多線程中,可能出現(xiàn)數(shù)據(jù)不一致;
因?yàn)闆]有全局的鎖,在清除完一個(gè)segments之后,正在清理下一個(gè)segments的時(shí)候,已經(jīng)清理segments可能又被加入了數(shù)據(jù),因此clear返回的時(shí)候,ConcurrentHashMap中是可能存在數(shù)據(jù)的。因此,clear方法是弱一致的。
ConcurrentHashMap的弱一致性主要是為了提升效率,是一致性與效率之間的一種權(quán)衡。要成為強(qiáng)一致性,就得到處使用鎖,甚至是全局鎖,這就與Hashtable和同步的HashMap一樣了。
而在迭代時(shí),ConcurrentHashMap使用了不同于傳統(tǒng)集合的快速失敗迭代器(見之前的文章《JAVA API備忘---集合》)的另一種迭代方式,我們稱為弱一致迭代器。在這種迭代方式中,當(dāng)iterator被創(chuàng)建后集合再發(fā)生改變就不再是拋出ConcurrentModificationException,取而代之的是在改變時(shí)new新的數(shù)據(jù)從而不影響原有的數(shù)據(jù),iterator完成后再將頭指針替換為新的數(shù)據(jù),這樣iterator線程可以使用原來老的數(shù)據(jù),而寫線程也可以并發(fā)的完成改變,更重要的,這保證了多個(gè)線程并發(fā)執(zhí)行的連續(xù)性和擴(kuò)展性,是性能提升的關(guān)鍵。
詳情:
ConcurrentHashMap基本思想:


ConcurrentHashMap數(shù)據(jù)結(jié)構(gòu)

ConcurrentHashMap的初始化


ConcurrentHashMap的get方法

ConcurrentHashMap的put方法

ConcurrentHashMap的size方法

ConcurrentHashMap的弱一致性
因?yàn)闆]有全局的鎖,在清除完一個(gè)segments之后,正在清理下一個(gè)segments的時(shí)候,已經(jīng)清理segments可能又被加入了數(shù)據(jù),因此clear返回的時(shí)候,ConcurrentHashMap中是可能存在數(shù)據(jù)的。因此,clear方法是弱一致的。
舉2個(gè)比較簡單的例子就可以看出ConcurrentHashMap的弱一致性。
第一個(gè),就是clear方法。假設(shè)線程1執(zhí)行到Segment[0].clear()的時(shí)候,Segment[0]中的元素元素已經(jīng)被clear,但是線程2此時(shí)可以在Segment[0]中增加元素(clear不需要加鎖),這樣線程1執(zhí)行結(jié)束的時(shí)候就ConcurrentHashMap并不是空的。

第二個(gè)例子,判斷key是否存在,不存在則進(jìn)行put。假設(shè)線程1判斷key不存在后掛起,線程2判斷key也不存在,然后線程2執(zhí)行put操作,之后線程1執(zhí)行put操作,此時(shí)線程2讀取到的會(huì)是線程1設(shè)置的值而不是線程2設(shè)置的值。當(dāng)然此時(shí)應(yīng)該是應(yīng)用putIfAbsent方法。
小結(jié)
ConcurrentHashMap在設(shè)計(jì)思路對效率和一致性上的平衡以及一些lock-free的方法非常值得借鑒,同時(shí)注意雖然類本身是線程安全的,但是不要認(rèn)為使用該類就一定是線程安全的。