源碼解析
ConcurrentMap 首先這個是一個接口,繼承了Map接口
public interface ConcurrentMap<K, V> extends Map<K, V>
這個其中除了重寫的方法外永高,主要擴(kuò)展了四個方法提针,這四個方法分別為:
V putIfAbsent(K key, V value);
新增一個元素,如果map中已經(jīng)存在了key值相等的遇骑,那么不替換揖曾,相當(dāng)于啥也沒干
boolean remove(Object key, Object value);
刪除一個元素,Map中key和value值都存在且相等時(shí)刪除练链,否則不刪除
boolean replace(K key, V oldValue, V newValue);
更新一個元素奴拦,Map中key和value值都存在且相等時(shí)更新,否則不更新
V replace(K key, V value);
也是更新一個元素绿鸣,與上一個的區(qū)別時(shí)不需要判斷value是否相等暂氯,只需判斷key存在時(shí)就替換value
下面我們來看看主要實(shí)現(xiàn)了ConcurrentMap接口的ConcurrentHashMap
ConcurrentHashMap
我們來看看四個方法的實(shí)現(xiàn)
首先是putIfAbsent
public V putIfAbsent(K key, V value) {
return putVal(key, value, true);
}
可以看到 其中其實(shí)是調(diào)用了putVal方法 其中參數(shù)onlyIfAbsent為ture痴施,也就是說究流,當(dāng)這個值為true時(shí)动遭,key存在時(shí)不替換原先的value,為false時(shí)才替換偷仿。
那么來看看putVal的具體實(shí)現(xiàn) 配上了注釋
final V putVal(K key, V value, boolean onlyIfAbsent) {
//首先判斷key和value是否為空绵估,為空拋出空指針異常
if (key == null || value == null) throw new NullPointerException();
//得到key的hash值
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
//判斷當(dāng)前的tab是否為空国裳,為空則初始化一個大小為16的tab
if (tab == null || (n = tab.length) == 0)
tab = initTable();
//判斷當(dāng)前tab下標(biāo)位置是否為null 為null直接插入
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
//給當(dāng)前節(jié)點(diǎn)上鎖,實(shí)現(xiàn)線程安全
synchronized (f) {
//再次判斷當(dāng)前的節(jié)點(diǎn)是否與之前一樣亿遂,因?yàn)榭赡茉谕粫r(shí)間map被其他線程修改過
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
//遍歷當(dāng)前節(jié)點(diǎn)
for (Node<K,V> e = f;; ++binCount) {
K ek;
//判斷當(dāng)前節(jié)點(diǎn)是否相等
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
//onlyIfAbsent根據(jù)一開始傳入的值來判斷是否更新value值渺杉,為true表示不更新直接跳過并退出
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
//如果沒找到是越,且當(dāng)前節(jié)點(diǎn)的下一個節(jié)點(diǎn)為空 則直接添加一個新的節(jié)點(diǎn)
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
//判斷如果當(dāng)前節(jié)點(diǎn)是否屬于紅黑樹
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
// 判斷是否需要轉(zhuǎn)為紅黑樹存儲
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
看看remove方法
public boolean remove(Object key, Object value) {
//判斷傳入的key是否為null
if (key == null)
throw new NullPointerException();
return value != null && replaceNode(key, null, value) != null;
}
可以看到 倚评,主要調(diào)用了 主要是調(diào)用了replaceNode方法
final V replaceNode(Object key, V value, Object cv) {
//計(jì)算key的hash值
int hash = spread(key.hashCode());
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
//判斷map中是否有存儲這個key值 主要為這個部分 (f = tabAt(tab, i = (n - 1) & hash))
if (tab == null || (n = tab.length) == 0 ||
(f = tabAt(tab, i = (n - 1) & hash)) == null)
break;
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
boolean validated = false;
//對當(dāng)前的節(jié)點(diǎn)上鎖天梧,實(shí)現(xiàn)線程安全
synchronized (f) {
//再次判斷當(dāng)前的節(jié)點(diǎn)是否與之前一樣,因?yàn)榭赡茉谕粫r(shí)間map被其他線程修改過
if (tabAt(tab, i) == f) {
if (fh >= 0) {
validated = true;
// 遍歷
for (Node<K,V> e = f, pred = null;;) {
K ek;
//進(jìn)行判斷冕香,首先進(jìn)行key的判斷
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
V ev = e.val;
//在進(jìn)行value的判斷后豫,
if (cv == null || cv == ev ||
(ev != null && cv.equals(ev))) {
oldVal = ev;
// 根據(jù)傳入的參數(shù)進(jìn)行判斷,是進(jìn)行修改或者是刪除操作
if (value != null)
e.val = value;
else if (pred != null)
pred.next = e.next;
else
setTabAt(tab, i, e.next);
}
break;
}
pred = e;
if ((e = e.next) == null)
break;
}
}
//這里判斷是否為紅黑樹焕襟,關(guān)于紅黑樹日后在進(jìn)行深入探討
else if (f instanceof TreeBin) {
validated = true;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(hash, key, null)) != null) {
V pv = p.val;
if (cv == null || cv == pv ||
(pv != null && cv.equals(pv))) {
oldVal = pv;
if (value != null)
p.val = value;
else if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
}
if (validated) {
if (oldVal != null) {
if (value == null)
addCount(-1L, -1);
return oldVal;
}
break;
}
}
}
return null;
}
在來看看replace方法
public boolean replace(K key, V oldValue, V newValue) {
if (key == null || oldValue == null || newValue == null)
throw new NullPointerException();
return replaceNode(key, newValue, oldValue) != null;
}
public V replace(K key, V value) {
if (key == null || value == null)
throw new NullPointerException();
return replaceNode(key, value, null);
}
其實(shí)內(nèi)部也就是調(diào)用了replaceNode方法
那么其中三個參數(shù)分別表示key值,newValue新的value拄衰,和oldValue 原先的值
當(dāng)newValue值為空時(shí)表示要刪除,當(dāng)newValue不為空表示要修改
總結(jié)
ConcurrentHashMap為什么效率比Hashtable來的高呢茫打?
因?yàn)镠ashtable內(nèi)部是對整個map進(jìn)行加鎖妖混,而ConcurrentHashMap內(nèi)部是對其中的一個Node節(jié)點(diǎn)進(jìn)行加鎖,因此當(dāng)進(jìn)行增刪改查時(shí)只要時(shí)為不同的一個Node的話抬旺,那么相互不影響祥楣,從而提升效率。