ConcurrentHashMap源码剖析02_putVal()方法详解

public V put(K key, V value) {
        return putVal(key, value, false);
    }

    /** Implementation for put and putIfAbsent */
    final V putVal(K key, V value, boolean onlyIfAbsent) {
    //key和value不能为null
        if (key == null || value == null) throw new NullPointerException();
        //让hash值的高位也能参与运算
        int hash = spread(key.hashCode());
        //当前k v封装成node之后,插入桶位之后在桶位中所属的下标位置
        //0:表示当前桶位为null
        //2:表示当前桶位元素可能已经树化成了红黑树
        int binCount = 0;
        //自旋
        for (Node<K,V>[] tab = table;;) {
        //f:桶位的头节点
        //n:散列表的长度
        //i:key通过寻址计算之后得到的桶位下标
        //fh:桶位头结点的哈希值
            Node<K,V> f; int n, i, fh;
            //如果条件成立,表示table未初始化
            if (tab == null || (n = tab.length) == 0)
            	//多线程竞争初始化数组
                tab = initTable();
                //如果table已经被初始化
                //获取当前桶位的头节点,如果为null,就将封装好的node放入
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            	//cas成功,break退出
            	//cas失败,说明已经有其他线程进入,所以继续自旋
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            //表示当前桶位头节点为FWD节点
            //表示map正在扩容过程中
            else if ((fh = f.hash) == MOVED)
            	//当前节点帮助map对象完成数据迁移
                tab = helpTransfer(tab, f);
                //当前桶位可能是链表节点也有也可能是红黑树节点
            else {
            	//当插入的key存在时,会将旧值赋值给oldVal,然后返回
                V oldVal = null;
                //给头节点加锁,理论上的 “头节点”
                synchronized (f) {
                //再次验证当前头节点是否为之前获得的头节点
                //避免其他线程将该桶位的头节点修改
                //条件成立说明加锁没问题
                    if (tabAt(tab, i) == f) {
                    //说明当前桶位是普通链表
                        if (fh >= 0) {
                        	//1、当前插入的key与链表当中的key都不一致,将key插入到链表末尾,bincount表示链表元素
                        	//2、key已经存在,当前插入操作就为替换,bincount表示冲突位置
                            binCount = 1;
                            //迭代循环
                            for (Node<K,V> e = f;; ++binCount) {
                            //当前循环节点的key
                                K ek;
                                //循环节点的哈希值和待插入元素的哈希值相同
                                //循环节点的key与待插入节点的key相同
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                     //将旧值赋值给oldVal
                                    oldVal = e.val;
                                    //替换,结束
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                //链表中没有相同的key
                                Node<K,V> pred = e;
                                //判断下一个节点是否为null,如果是,添加到队尾
                                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<=1有其他含义
                            binCount = 2;
                            //如果节点冲突,则返回冲突节点
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                //可能是红黑树也有可能是链表
                if (binCount != 0) {
                	//bincount>=8链表转为红黑树
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                        //返回旧值
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        //统计当前table一共有多少数据
        //判断是否达到扩容标准
        addCount(1L, binCount);
        return null;
    }

/**
     * Initializes table, using the size recorded in sizeCtl.
     */
    private final Node<K,V>[] initTable() {
    //table引用
    //表示临时sizeCTL的值
        Node<K,V>[] tab; int sc;
        //自旋,条件是散列表未初始化
        while ((tab = table) == null || tab.length == 0) {
        //-1:当前table正在初始化了
        //其他:当前数组正在进行扩容
            if ((sc = sizeCtl) < 0)
            //大概率为-1,其他线程正在进行初始化
                Thread.yield(); // lost initialization race; just spin
                //其他情况,==0或者>0
                //==0,初始化使用默认常量大小
                //>0:如果已经初始化,表示下次扩容时的触发条件
                //如果CAS成功,把SIZECTL设置为-1
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                //为什么又要判断一次?
                //防止其他线程已经初始化完毕了,当前线程再进来再次初始化丢失数据
                    if ((tab = table) == null || tab.length == 0) {
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        @SuppressWarnings("unchecked")
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                        table = tab = nt;
                        //n >>> 2?
                        //sc = n - n*1/4 = 0.75n
                        //表示下次扩容时的触发条件
                        sc = n - (n >>> 2);
                    }
                } finally {
                	//两种情况:
                	//1、进来的线程成功初始化了数组,所以把sizeCtl当作下一次扩容的阈值
                	//2、进来的线程没有获取到锁,所以需要将sizeCtl修改为原来的值
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                        Node<K,V> c, Node<K,V> v) {
        //i表示index,c表示期望值,v表示新值
        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值