ConcurrentHashMap扩容操作链表重分配

 ConcurrentHashMap中和HashMap一样,当容量不足时,需要进行扩容操作。由于ConcurrentHashMap需要支持并发下的扩容操作,因此要比HashMap复杂很多。下面是对扩容操作时,原table某处i位置上的链表从新分配到新table位置i和n+i部分的分析。

Node<K,V> ln, hn;
if (fh >= 0) {
    // fh的第n位若为0,保持原位置fh & (n - 1) = i,对应链表ln,
    // 否则移动到新位置fh & (n * 2 - 1) = n + i ,对应链表hn
    int runBit = fh & n; // runBit为0或n(n是2的次幂,二进制只有一位为1)
    // lastRun用来从原链表中寻找这样的结点:此结点以及之后的结点的hash
    // 值,第n位要么全为0,要么全为1,这些结点构成的子链表(连续的,且在原链表末尾)可以原封不动的
    // 放入到新table中i位置(hash值n位为0),或n+i位置(hash值n位为1)
    Node<K,V> lastRun = f;
    for (Node<K,V> p = f.next; p != null; p = p.next) { // 寻找这样的lastRun结点
        int b = p.hash & n;
        if (b != runBit) { // 反复的寻找,直到某个结点(lastRun)之后b和runBit一直都相等
            runBit = b;
            lastRun = p;
        }
    }
    if (runBit == 0) { // 子链表中结点hash值第n位全为0,保存到ln中(i位置)
        ln = lastRun;
        hn = null;
    }
    else { // 子链表中结点hash值第n位全为1,保存到hn中(n+i位置)
        hn = lastRun;
        ln = null;
    }
    for (Node<K,V> p = f; p != lastRun; p = p.next) { // 将原链表中lastRun结点之前的部分分别加入ln和hn
        int ph = p.hash; K pk = p.key; V pv = p.val;
        // 从前往后遍历这些结点,不断加入到ln或hn的头部,可以看出
        // 这些结点是被反序(相对原链表)加入到新链表中的
        if ((ph & n) == 0)
            ln = new Node<K,V>(ph, pk, pv, ln);
        else
            hn = new Node<K,V>(ph, pk, pv, hn);
    }
    setTabAt(nextTab, i, ln); // 将头结点ln放到新表中i位置
    setTabAt(nextTab, i + n, hn); // 将头结点hn放到新表中i+n位置
    
    // 该部分代码都是在原表i处链表头结点加锁的情况下运行的,释放锁钱,
    // 将旧表的i位置设置为ForwardingNode结点表示此处已经修改完毕,其他线程不再修改
    setTabAt(tab, i, fwd);
    advance = true;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值