concurrethashmap的put操作

put操作大致可分为以下几个步骤:

1·计算key的hash值,即调用spread()方法计算hash值;

2·获取hash值对应的Node节点位置,此时通过一个循环实现。有以下几种情况:

3·A`如果table表为空,则首先进行初始化操作,初始化之后再次进入循环获取Node节点的位置;

3·B`如果table不为空,但没有找到key对应的Node节点,则直接调用casTabAt()方法插入一个新节点,此时使用CAS进行插入,
CAS失败,说明有其它线程提前插入了,CAS会尝试自旋重新插入;
,因为第一次CAS失败,CAS发现数据与原数据不同,会自旋重新插入,第二次进入修改数据为想要插入的数据
CAS成功,则直接插入并计算addCount,判断是否需要把链表升级为红黑树。

3·C`如果table不为空,且key对应的Node节点也不为空,但Node头节点的hash值为MOVED(-1)(hash值默认为MOVED(-1)),则表示需要扩容,此时调用helpTransfer()方法进行扩容;(第五步补充:如果头节点的hash值为-1,说明当前f是ForwardingNode节点,意味有其它线程正在扩容,则一起进行扩容操作。我的理解是第一次扩容,头节点才为-1

3·D`其他情况下,则直接向Node中插入一个新Node节点,此时需要对这个Node链表或红黑树通过synchronized加锁。
插入元素后,调用addCount()方法记录table中元素的数量,判断对应的Node结构是否需要改变结构,如果需要则调用treeifyBin()方法将Node链表升级为红黑树结构;

synchronized (f) {
    if (tabAt(tab, i) == f) {
        if (fh >= 0) {
            binCount = 1;
            for (Node<K,V> e = f;; ++binCount) {
                K ek;
                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;
            }
        }
    }
}

put时,内部使用的方法


    //以volatile读的方式读取table数组中的元素
    static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
        return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
    }
    //以volatile写的方式,将元素插入table数组(修改数据使用)
    static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
        U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
    }
    //以CAS的方式,将元素插入table数组(上述步骤3·B)
    static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                        Node<K,V> c, Node<K,V> v) {
        //原子的执行如下逻辑:如果tab[i]==c,则设置tab[i]=v,并返回ture.否则返回false
        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
    }

————————————————
推荐链接:JDK1.8,concurrethashmap详解
最佳推荐:https://www.jianshu.com/p/5bc70d9e5410

也可以参考https://www.jianshu.com/p/c0642afe03e0

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值