ConcurrentHashMap
是做Java開發(fā)必須要掌握的類的用法之一豫尽,它彌補(bǔ)了HashTable
在并發(fā)環(huán)境下的性能的不足钢猛。引用分離鎖的概念,極大地增強(qiáng)了并發(fā)性能。
ConcurrentHashMap 存儲(chǔ)結(jié)構(gòu)圖
ConcurrentHashMap
相比于HashMap
新增了一個(gè)segments數(shù)組酒繁,每一個(gè)數(shù)組分別對(duì)應(yīng)一把鎖,ConcurrentHashMap
默認(rèn)情況下這個(gè)數(shù)組長度為16控妻,即在理想狀態(tài)下ConcurrentHashMap
可以支持16個(gè)線程無鎖安全訪問數(shù)據(jù)欲逃。
舉個(gè)例子:當(dāng)有一個(gè)線程正在修改segments[0]中的內(nèi)容時(shí),同時(shí)另一個(gè)線程要查詢的value剛好在segment[2]中饼暑,那么segment[2]就不會(huì)加鎖稳析。
ConcurrentHashMap中的HashEntry內(nèi)部類
static final class HashEntry<K,V> {
final K key;
final int hash;
volatile V value;
final HashEntry<K,V> next;
...
}
HashMap
是以數(shù)組加鏈表的形式存儲(chǔ)數(shù)據(jù)的,ConcurrentHashMap
當(dāng)然也不例外弓叛,上面這個(gè)是ConcurrentHashMap
中用作鏈表存儲(chǔ)的內(nèi)部類彰居。它除了value 其余變量全部設(shè)為final型。
ConcurrentHashMap 中的數(shù)據(jù)交互
其實(shí)分析ConcurrentHashMap
最重要的就是分析它是怎么做數(shù)據(jù)交互的撰筷,下面將會(huì)依次分析ConcurrentHashMap
中數(shù)據(jù)是怎么存儲(chǔ)和刪除
1.如何put
由于HashEntry
中 next變量被聲明為了final陈惰,那么要增加一個(gè)新的Entry那就只能從頭部添加,如下圖:
它會(huì)將新new出來的HashEntry
指向原本數(shù)組指向的表頭元素毕籽,然后將數(shù)組指向這個(gè)類抬闯。
2.如何delete
在ConcurrentHashMap
中刪除一個(gè)元素可不像傳統(tǒng)鏈表刪除一個(gè)元素時(shí)只需要修改下指針位置那么簡單。因?yàn)?code>HashEntry中next為final類型关筒,所以位于要?jiǎng)h除的元素之前的元素必須要重新new一次溶握,再依次指定next。如下圖:
ConcurrentHashMap 中的數(shù)據(jù)臟讀
參照?qǐng)D1-3蒸播,我們假設(shè)一種情況睡榆,當(dāng)線程1想要讀取e3的數(shù)據(jù)萍肆,此時(shí)讀取到了e1位置;而與此同時(shí)線程2正在執(zhí)行刪除e3操作胀屿,那么就有可能線程2執(zhí)行完刪除操作后塘揣,線程1仍然能讀取出e3的數(shù)據(jù)。這就是ConcurrentHashMap中的數(shù)據(jù)臟讀宿崭,所以ConcurrentHashMap不適合存儲(chǔ)敏感類型的數(shù)據(jù)亲铡。
必須應(yīng)該了解的Clear方法
首先看一下ConcurrentHashMap中的clear方法的源碼
public void clear() {
final Segment<K,V>[] segments = this.segments;
for (int j = 0; j < segments.length; ++j) {
Segment<K,V> s = segmentAt(segments, j);
if (s != null)
s.clear();
}
}
它在for循環(huán)中逐個(gè)去清除Segment下的數(shù)據(jù)胧后,如果不為空崔步,則調(diào)用segment的clear方法毅访。
基于這個(gè)方法攘残,有時(shí)候就會(huì)出現(xiàn)一種比較奇怪的現(xiàn)象:某一個(gè)線程剛存進(jìn)去的key-value,馬上就沒有了豹储,并且存進(jìn)去之后并沒有任何線程調(diào)用clear等類似方法。
結(jié)合clear的代碼思考一下不難理解,這種情況是在put一個(gè)value之前恰好有一個(gè)線程調(diào)用了clear方法蜕劝,在這個(gè)方法逐個(gè)清理segment時(shí),put的這個(gè)value剛好保存在了將要被clear但還沒有被clear的segment上轰异,所以這個(gè)value就會(huì)立馬被清理掉岖沛。
總結(jié)
ConcurrentHashMap
是一個(gè)并發(fā)情況下線程安全的HashMap,但是它的安全并不是那么絕對(duì)搭独,在一些特殊情況下仍然有可能會(huì)發(fā)生數(shù)據(jù)臟讀婴削,數(shù)據(jù)丟失等情況。所以在我們使用ConcurrentHashMap
時(shí)一定要了解它的一些特性牙肝,這樣我們才能在使用它的時(shí)候不會(huì)產(chǎn)生一些意料之外的問題唉俗。