1.ConcurrentHashMap 的get方法并未加锁;
原理:Node对象中的key是final修饰 ,value是volatile修饰;
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
//不可变量
final K key;
//可见性
volatile V val;
//volitile 修饰对象或数组会保持地址的可见性
volatile Node<K,V> next;
2.JDK1.8ConcurrentHashMap 的computerIfAbsent方法存在性能问题;并且该方法可能存在死锁。
(在mybatis 3.5.X中该方法使用频率较高,高并发下存在性能问题,3.5.7会进行修复)
mybatis3.5.6版本该方法使用位置 DefaultReflectorFactory findForClass方法:
问题描述:https://zhuanlan.zhihu.com/p/364340936
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
//每次url数据库请求都会到这,但是key都是存在的,会有性能问题
return reflectorMap.computeIfAbsent(type, Reflector::new);
} else {
return new Reflector(type);
}
}
该方法的含义是对Map 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hasMap 中,如存在则返回原始值,不做覆盖操作。
jdk1.8 该方法源码如下:(1.9以后版本已修复)
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
if (key == null || mappingFunction == null)
throw new NullPointerException();
int h = spread(key.hashCode());
V val = null;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
//当该key不存在是插入
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
boolean added = false;
//当key存在时,也是使用synchronized修饰,会有性能问题;*******
//Node本身具有可见性,读取时不需要加锁控制********
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek; V ev;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
val = e.val;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
if ((val = mappingFunction.apply(key)) != null) {
added = true;
pred.next = new Node<K,V>(h, key, val, null);
}
break;
}
}
}
else if (f instanceof TreeBin) {
binCount = 2;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(h, key, null)) != null)
val = p.val;
else if ((val = mappingFunction.apply(key)) != null) {
added = true;
t.putTreeVal(h, key, val);
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (!added)
return val;
break;
}
}
}
if (val != null)
addCount(1L, binCount);
return val;
}
jdk11源码如下:已修复上述问题
...
//上述else(key存在时)分支修复后如下
else {
int fh;
if ((fh = f.hash) == -1) {
tab = this.helpTransfer(tab, f);
} else {
//不再用synchronized修饰,提高性能
Object fk;
Object fv;
if (fh == h && ((fk = f.key) == key || fk != null && key.equals(fk)) && (fv = f.val) != null) {
return fv;
}
boolean added = false;
synchronized(f) {
if (tabAt(tab, i) == f) {
if (fh < 0) {
....