一、HashMap、HashTable与ConcurrentHashMap对比
二、ConcurrentHashMap源码解读
2.1 volatile修饰的节点数组
//ConcurrentHashMap使用volatile修饰节点数组,保证其可见性,禁止指令重排。
transient volatile Node<K,V>[] table;
2.2 ConcurrentHashMap的put()方法
final V putVal(K key, V value, boolean onlyIfAbsent) {
//key和value有一个为空就抛空指针异常
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
//新增一个节点后进入死循环
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
//flag1:如果节点数组未初始化,则进行初始化
if (tab == null || (n = tab.length) == 0)
tab = initTable();
//flag2:key的hash值来判断table中是否存在相同的key,如果不存在,执行CAS操作,【不加锁】
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
}
//flag3:判断是否在扩容,moved=-1
//如果在扩容中,帮助其扩容,完成此次循环
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
//flag4:key的hash值位置不为null(在flag2出判断hash值为null时直接做插入)
//发生了hash冲突,节点就要通过链表的形式存储这个插入的新值。Node类是有next字段的,指向链表的下一个位置,新节点就往这插。
else {
V oldVal = null;
//排它锁,只有在发生hash冲突的时候才加了排它锁
synchronized (f) {
//重新判断当前节点是不是flag2判断过的节点,如果不是,表示节点被其他线程改过了,进入下一轮循环
if (tabAt(tab, i) == f) {
//如果是flag2判断过的节点,再次判断是否在扩容中,如果flag3正在扩容,进入下一轮循环
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
//找到一个hash值相同,且key也完全相同的节点,更新这个节点
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
//找不到,往链表最后插入这个新节点
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
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) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
2.3 ConcurrentHashMap的get()方法
不加任何锁,原因:volatile修饰table。
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
引用:https://blog.csdn.net/qq_42068856/article/details/126091526