Java之currenHashMap
? ? ? ? currenHashMap是jkd1.5引入的,其特點(diǎn)是:效率比Hashtable高,并發(fā)性比HashMap好。結(jié)合了兩者的特點(diǎn)。
??????? ConcurrentHashMap是一個(gè)線程安全的Hash Table,它的主要功能是提供了一組和HashTable功能相同但是線程安全的方法。ConcurrentHashMap可以做到讀取數(shù)據(jù)不加鎖,并且其內(nèi)部的結(jié)構(gòu)可以讓其在進(jìn)行寫操作的時(shí)候能夠?qū)㈡i的粒度保持地盡量地小,不用對(duì)整個(gè)ConcurrentHashMap加鎖。
??????? ConcurrentHashMap為了提高本身的并發(fā)能力,在內(nèi)部采用了一個(gè)叫做Segment的結(jié)構(gòu),一個(gè)Segment其實(shí)就是一個(gè)類Hash Table的結(jié)構(gòu),Segment內(nèi)部維護(hù)了一個(gè)鏈表數(shù)組,我們用下面這一幅圖來看下ConcurrentHashMap的內(nèi)部結(jié)構(gòu):
??????? 從上面的結(jié)構(gòu)我們可以了解到,ConcurrentHashMap定位一個(gè)元素的過程需要進(jìn)行兩次Hash操作,第一次Hash定位到Segment,第二次Hash定位到元素所在的鏈表的頭部,因此,這一種結(jié)構(gòu)的帶來的副作用是Hash的過程要比普通的HashMap要長(zhǎng),但是帶來的好處是寫操作的時(shí)候可以只對(duì)元素所在的Segment進(jìn)行加鎖即可,不會(huì)影響到其他的Segment,這樣,在最理想的情況下,ConcurrentHashMap可以最高同時(shí)支持Segment數(shù)量大小的寫操作(剛好這些寫操作都非常平均地分布在所有的Segment上),所以,通過這一種結(jié)構(gòu),ConcurrentHashMap的并發(fā)能力可以大大的提高。
??????? HashMap中未進(jìn)行同步考慮,而Hashtable則使用了synchronized,帶來的直接影響就是可選擇,我們可以在單線程時(shí)使用HashMap提高效率,而多線程時(shí)用Hashtable來保證安全。通過分析Hashtable就知道,synchronized是針對(duì)整張Hash表的,即每次鎖住整張表讓線程獨(dú)占,安全的背后是巨大的浪費(fèi)。
?
??????? 左邊便是Hashtable的實(shí)現(xiàn)方式---鎖整個(gè)hash表;而右邊則是ConcurrentHashMap的實(shí)現(xiàn)方式---段鎖。它使用了多個(gè)鎖來控制對(duì)hash表的不同部分進(jìn)行的修改。?ConcurrentHashMap將hash表分為16段(默認(rèn)值),諸如get,put,remove等常用操作只鎖當(dāng)前需要用到的段。試想,原來 只能一個(gè)線程進(jìn)入,現(xiàn)在卻能同時(shí)16個(gè)寫線程進(jìn)入(寫線程才需要鎖定,而讀線程幾乎不受限制,之后會(huì)提到),并發(fā)性的提升是顯而易見的。
?? ? ? ? ConcurrentHashMap的讀取并發(fā),因?yàn)樵谧x取的大多數(shù)時(shí)候都沒有用到鎖定,所以讀取操作幾乎是完全的并發(fā)操作,而寫操作鎖定的粒度又非常細(xì),比起之前又更加快速(這一點(diǎn)在桶更多時(shí)表現(xiàn)得更明顯些)。只有在求size()和containsValue()等操作時(shí)才需要鎖定整個(gè)表。它們可能需要鎖定整個(gè) 表而而不僅僅是某個(gè)段,這需要按順序鎖定所有段,操作完畢后,又按順序釋放所有段的鎖(防止死鎖)。
??????? 讀是否要加鎖,因?yàn)樽x寫會(huì)發(fā)生沖突?ConcurrentHashMap完全允許多個(gè)讀操作并發(fā)進(jìn)行,讀操作并不需要加鎖。如果使 用傳統(tǒng)的技術(shù),如HashMap中的實(shí)現(xiàn),如果允許可以在hash鏈的中間添加或刪除元素,讀操作不加鎖將得到不一致的數(shù)據(jù)。 ConcurrentHashMap實(shí)現(xiàn)技術(shù)是保證HashEntry幾乎是不可變的。HashEntry代表每個(gè)hash鏈中的一個(gè)節(jié)點(diǎn),其結(jié)構(gòu)如下所 示:
static final class HashEntry<K,V> {final K key;
final int hash;
volatile V value;
final HashEntry<K,V> next;
}
? ? ? 可以看到HashEntry的一個(gè)特點(diǎn),除了value以外,其他的幾個(gè)變量都是final的,這樣做是為了防止鏈表結(jié)構(gòu)被破壞,出現(xiàn)ConcurrentModification的情況。為了確保讀操作能夠看到最新的值,將value設(shè)置成volatile,這避免了加鎖。在當(dāng)前的Java內(nèi)存模型下,線程可以把變量保存在本地內(nèi)存(比如機(jī)器的寄存器)中,而不是直接在主存中進(jìn)行讀寫。這就可能造成一個(gè)線程在主存中修改了一個(gè)變量的值,而另外一個(gè)線程還繼續(xù)使用它在寄存器中的變量值的拷貝,造成數(shù)據(jù)的不一致。volatile關(guān)鍵字指示JVM,這個(gè)變量是不穩(wěn)定的,每次使用它都到主存中進(jìn)行讀取。一般說來,多任務(wù)環(huán)境下各任務(wù)間共享的標(biāo)志都應(yīng)該加volatile修飾。Volatile修飾的成員變量在每次被線程訪問時(shí),都強(qiáng)迫從共享內(nèi)存中重讀該成員變量的值。而且,當(dāng)成員變量發(fā)生變化時(shí),強(qiáng)迫線程將變化值回寫到共享內(nèi)存。這樣在任何時(shí)刻,兩個(gè)不同的線程總是看到某個(gè)成員變量的同一個(gè)值。
--------------------------------------------------------------------
PS:?歡迎關(guān)注公眾號(hào)"Devin說",會(huì)不定期更新Java相關(guān)技術(shù)知識(shí)。
--------------------------------------------------------------------
其他請(qǐng)參考:http://www.cnblogs.com/maxupeng/archive/2011/06/26/2090517.html
轉(zhuǎn)載于:https://www.cnblogs.com/devinzhang/archive/2012/02/24/2366678.html
總結(jié)
以上是生活随笔為你收集整理的Java之currenHashMap的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我国农业科学需要在哪些方面研发?
- 下一篇: 本人98年户籍由湖南当地农村转入学校,0