java8 ConcurrentHashMap源码学习2
上文: java8 ConcurrentHashMap源码学习.
ConcurrentHashMap
之前发现常用的remove方法还有helptransfer并没有整理出来, 这里把学习心得整理一下, 顺便把get也贴上来
remove
其实这里跟put一样也是直接引用另一个方法
public V remove(Object key) {
return replaceNode(key, null, null);
}
这里看到用了replaceNode方法入参是key, null, null
下面的代码里可以看见这三个参数的名称
key就是map中的key, value就是要替换的值, cv就是要替换的key的value
如果value为空, 那么那么就是替换key的value而不是做删除
如果cv为空那么就不判断oldValue直接进入替换或者删除操作, 否则判断cv是否与oldValue相等
final V replaceNode(Object key, V value, Object cv) {
int hash = spread(key.hashCode());
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0 ||
(f = tabAt(tab, i = (n - 1) & hash)) == null)
break;
// MOVED代表当前有迁移工作, 加入迁移
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
// 一个标志位, 标志是否有进行替换或者删除工作
boolean validated = false;
synchronized (f) {
// 确保上锁前后数据一致
if (tabAt(tab, i) == f) {
// 置于这里为什么fh>=0才是链表, 因为在构造红黑树的时候
// 构造函数会将hash变量设置为TREEBIN, 而TREEBIN的值是-2
if (fh >= 0) {
validated = true;
for (Node<K,V> e = f, pred = null;;) {
K ek;
// 找到这个key
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
V ev = e.val;
// 上文提到的cv判断
if (cv == null || cv == ev ||
(ev != null && cv.equals(ev))) {
oldVal = ev;
// 上文提到的value判断替换或者删除
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<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
// 判断红黑树是否存在这个key
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;
// 这里removeTreeNode返回的bool不是代表删除的成功与否
// 这里返回的是是否需要将红黑树转换为链表
// 下文会写到这个方法
else if (t.removeTreeNode(p))
// 用untreeify方法将红黑树链表化
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
}
// 已经操作成功
if (validated) {
if (oldVal != null) {
// 这里表示这是个删除操作, 将table中的元素计数器-1
if (value == null)
addCount(-1L, -1);
return oldVal;
}
break;
}
}
}
return null;
}
removeTreeNode
主要讲一下返回的true和false, 红黑树操作按下不表, (因为👴还不太清楚, 下次一定学
final boolean removeTreeNode(TreeNode<K,V> p) {
TreeNode<K,V> next = (TreeNode<K,V>)p.next;
TreeNode<K,V> pred = p.prev; // unlink traversal pointers
TreeNode<K,V> r, rl;
// 在TreeBin里面是有first属性的, 因为在treeifyBin这个方法中
// 先构成双链表, 然后传入TreeBin的构造器中构造红黑树
if (pred == null)
// 这里pred=null代表着要删除的p是第一个结点, first后移
first = next;
else
// 否则pred指向p的下一个
pred.next = next;
if (next != null)
// 双链表操作
next.prev = pred;
// 特判first为空的情况, 直接返回true表示将红黑树链表化, 出来的当然也是null
if (first == null) {
root = null;
return true;
}
// 这里是判断红黑树大小, 没有采用结点个数感觉挺奇怪的
// 因为就算红黑树中有十个结点也可以构成true的情况, 我下面贴个图
if ((r = root) == null || r.right == null || // too small
(rl = r.left) == null || rl.left == null)
return true;
// 给红黑树上锁进行删除操作, 这下面涉及的就是红黑树的删除操作, 双链表操作上面做完了
lockRoot();
try {
TreeNode<K,V> replacement;
TreeNode<K,V> pl = p.left;
TreeNode<K,V> pr = p.right;
if (pl != null && pr != null) {
TreeNode<K,V> s = pr, sl;
while ((sl = s.left) != null) // find successor
s = sl;
boolean c = s.red; s.red = p.red; p.red = c; // swap colors
TreeNode<K,V> sr = s.right;
TreeNode<K,V> pp = p.parent;
if (s == pr) { // p was s's direct parent
p.parent = s;
s.right = p;
}
else {
TreeNode<K,V> sp = s.parent;
if ((p.parent = sp) != null) {
if (s == sp.left)
sp.left = p;
else
sp.right = p;
}
if ((s.right = pr) != null)
pr.parent = s;
}
p.left = null;
if ((p.right = sr) != null)
sr.parent = p;
if ((s.left = pl) != null)
pl.parent = s;
if ((s.parent = pp) == null)
r = s;
else if (p == pp.left)
pp.left = s;
else
pp.right = s;
if (sr != null)
replacement = sr;
else
replacement = p;
}
else if (pl != null)
replacement = pl;
else if (pr != null)
replacement = pr;
else
replacement = p;
if (replacement != p) {
TreeNode<K,V> pp = replacement.parent = p.parent;
if (pp == null)
r = replacement;
else if (p == pp.left)
pp.left = replacement;
else
pp.right = replacement;
p.left = p.right = p.parent = null;
}
root = (p.red) ? r : balanceDeletion(r, replacement);
if (p == replacement) { // detach pointers
TreeNode<K,V> pp;
if ((pp = p.parent) != null) {
if (p == pp.left)
pp.left = null;
else if (p == pp.right)
pp.right = null;
p.parent = null;
}
}
} finally {
// 解锁
unlockRoot();
}
// 做红黑树的结构检查
assert checkInvariants(root);
return false;
}
图贴上面了, 如果这一步理解有误, 希望评论区可以指出
helpTransfer
算是最短的方法了吧??? 因为逻辑跟transfer里面差不多, 所以上次就没去搭理
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
Node<K,V>[] nextTab; int sc;
// 判断当前tab不为空, 并且传入结点正在迁移, 获得扩容好的nextTable
if (tab != null && (f instanceof ForwardingNode) &&
(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
// 根据tab.length算出一个高十六位负数
int rs = resizeStamp(tab.length);
// 循环判断获得的数据是否不变, sizeCtl<0则说明正在迁移
while (nextTab == nextTable && table == tab &&
(sc = sizeCtl) < 0) {
// 迁移工作已经完成, 退出循环
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || transferIndex <= 0)
break;
// 线程参与迁移, cas对sc+1表示参与迁移的线程数+1
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
transfer(tab, nextTab);
break;
}
}
return nextTab;
}
return table;
}
get
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
// cas获取当前的key所在的桶, 保证这个桶的数据是最新的
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;
}
之前find点进去还是链表的操作, 吓了一跳, 结果发现不是在TreeNode里面的, 下面才是真正使用的find,
堂堂正正的红黑树搜索
Node<K,V> find(int h, Object k) {
return findTreeNode(h, k, null);
}
后记
没有后记了, 如果还有下一篇再写这篇后记