源码解析
ConcurrentMap 首先这个是一个接口,继承了Map接口
public interface ConcurrentMap
这个其中除了重写的方法外,主要扩展了四个方法,这四个方法分别为:
V putIfAbsent(K key, V value);
新增一个元素,如果map中已经存在了key值相等的,那么不替换,相当于啥也没干
boolean remove(Object key, Object value);
删除一个元素,Map中key和value值都存在且相等时删除,否则不删除
boolean replace(K key, V oldValue, V newValue);
更新一个元素,Map中key和value值都存在且相等时更新,否则不更新
V replace(K key, V value);
也是更新一个元素,与上一个的区别时不需要判断value是否相等,只需判断key存在时就替换value
下面我们来看看主要实现了ConcurrentMap接口的ConcurrentHashMap
ConcurrentHashMap
我们来看看四个方法的实现
首先是putIfAbsent
public V putIfAbsent(K key, V value) {
return putVal(key, value, true);
}
可以看到 其中其实是调用了putVal方法 其中参数onlyIfAbsent为ture,也就是说,当这个值为true时,key存在时不替换原先的value,为false时才替换。
那么来看看putVal的具体实现 配上了注释
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[] tab = table;;) {
Node f; int n, i, fh;
//判断当前的tab是否为空,为空则初始化一个大小为16的tab
if (tab == null || (n = tab.length) == 0)
tab = initTable();
//判断当前tab下标位置是否为null 为null直接插入
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node(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;
//给当前节点上锁,实现线程安全
synchronized (f) {
//再次判断当前的节点是否与之前一样,因为可能在同一时间map被其他线程修改过
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
//遍历当前节点
for (Node e = f;; ++binCount) {
K ek;
//判断当前节点是否相等
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
//onlyIfAbsent根据一开始传入的值来判断是否更新value值,为true表示不更新直接跳过并退出
if (!onlyIfAbsent)
e.val = value;
break;
}
Node pred = e;
//如果没找到,且当前节点的下一个节点为空 则直接添加一个新的节点
if ((e = e.next) == null) {
pred.next = new Node(hash, key,
value, null);
break;
}
}
}
//判断如果当前节点是否属于红黑树
else if (f instanceof TreeBin) {
Node p;
binCount = 2;
if ((p = ((TreeBin)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
// 判断是否需要转为红黑树存储
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;
}
可以看到 ,主要调用了 主要是调用了replaceNode方法
final V replaceNode(Object key, V value, Object cv) {
//计算key的hash值
int hash = spread(key.hashCode());
for (Node[] tab = table;;) {
Node 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;
//对当前的节点上锁,实现线程安全
synchronized (f) {
//再次判断当前的节点是否与之前一样,因为可能在同一时间map被其他线程修改过
if (tabAt(tab, i) == f) {
if (fh >= 0) {
validated = true;
// 遍历
for (Node e = f, pred = null;;) {
K ek;
//进行判断,首先进行key的判断
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
V ev = e.val;
//在进行value的判断,
if (cv == null || cv == ev ||
(ev != null && cv.equals(ev))) {
oldVal = ev;
// 根据传入的参数进行判断,是进行修改或者是删除操作
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;
}
}
//这里判断是否为红黑树,关于红黑树日后在进行深入探讨
else if (f instanceof TreeBin) {
validated = true;
TreeBin t = (TreeBin)f;
TreeNode 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);
}
其实内部也就是调用了replaceNode方法
那么其中三个参数分别表示key值,newValue新的value,和oldValue 原先的值
当newValue值为空时表示要删除,当newValue不为空表示要修改
总结
ConcurrentHashMap为什么效率比Hashtable来的高呢?
因为Hashtable内部是对整个map进行加锁,而ConcurrentHashMap内部是对其中的一个Node节点进行加锁,因此当进行增删改查时只要时为不同的一个Node的话,那么相互不影响,从而提升效率。