ConcurrentHashMap put(),initTable()

 put()

package leetcode0606.JUC.ConcurrentHashMap;

import java.util.concurrent.ConcurrentHashMap;

public class _put {


    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();
        //  调用扰动哈希函数
        int hash = spread(key.hashCode());
        //  binCount  表示 k-v 封装为Node ,插入到指定桶位后,在桶位中所属链表的下标位置
        /*
            0  ---  当前桶位 为null,node可以直接放入
            2  ---  当前 桶位 可能为红黑树
         */
        int binCount = 0;
        // 自旋
        for (ConcurrentHashMap.Node<K,V>[] tab = table;;) {
            //  f  某个桶位的头节点
            //  n   table长度
            //  i   桶位下标
            //  fh  头节点的哈希值
            ConcurrentHashMap.Node<K,V> f; int n, i, fh;
            //  CASE1    table 表未初始化
            if (tab == null || (n = tab.length) == 0)
                //  多个线程并发地初始化,拿到tab,重新自旋
                tab = initTable();
            //  CASE2    table已经初始化了,指定位置为null,可尝试插入
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                //  前置条件:当前桶位为null,多个线程进行竞争地CAS操作
                if (casTabAt(tab, i, null,
                        new ConcurrentHashMap.Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }

            /*
            头节点 不为空
             */

            //  CASE3   头节点 是一个 FWD 节点 , 即正在扩容中,此线程有义务协助扩容
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);

            //  CASE4   当前桶位  可能是链表  也可能是  就红黑树代理节点 treeBin
            else {
                // 当插入key存在时,会将 旧值 赋给 oldVal,并返回。
                V oldVal = null;
                // f 头节点
                synchronized (f) {
                    // 为了防止头节点被修改了,如果被修改了,锁都加错了
                    if (tabAt(tab, i) == f) {
                        // fh >= 0  表示当前桶为 普通链表
                        if (fh >= 0) {
                            /*
                            binCount = 1
                            当前插入key与链表当中所有key都不一样,将插入元素追加到末尾,binCount表示链表长度
                            当前插入key与链表当中所有key有冲突,替换value,binCount-1 表示冲突位置
                             */
                            binCount = 1;


                            for (ConcurrentHashMap.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;
                                }
                                // 未发生冲突,遍历链表
                                ConcurrentHashMap.Node<K,V> pred = e;
                                if ((e = e.next) == null) {
                                    pred.next = new ConcurrentHashMap.Node<K,V>(hash, key,
                                            value, null);
                                    break;
                                }
                            }
                        }

                        //  前置条件: f 桶位不是链表
                        else if (f instanceof ConcurrentHashMap.TreeBin) {
                            ConcurrentHashMap.Node<K,V> p;
                            //  由于binCount <=1 有特殊含义,所以设置为2
                            binCount = 2;
                            // 条件成立:当前插入节点与红黑树某个节点发生冲突。
                            if ((p = ((ConcurrentHashMap.TreeBin<K,V>)f).putTreeVal(hash, key,
                                    value)) != null) {

                                // 新值代替旧值
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                // 说明当前桶位不为null,可能是链表,也可能是红黑树
                if (binCount != 0) {
                    // 如果 binCount >=8 ,表示处理的桶位一定是链表,需要执行树化的方法。
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    //  插入数据时发生冲突,返回旧值
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        //  addCount的作用
        //  1.统计当前table一共有多少数据
        //  2.判断是否达到扩容阈值标准,触发扩容
        addCount(1L, binCount);
        return null;
    }
}

initTable()

package leetcode0606.JUC.ConcurrentHashMap;

import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

public class _initTable {


    // 多线程场景下地初始化
    private final ConcurrentHashMap.Node<K,V>[] initTable() {

        //  tab 表示 table的引用
        //   sc  表示  sizeCtl 的引用
        ConcurrentHashMap.Node<K,V>[] tab; int sc;
        // 循坏的条件是 当前的table未初始化
        while ((tab = table) == null || tab.length == 0) {
            /*
            sizeCtl < 0   大概率是-1
            -1 表示table正在初始化(有其它线程在创建table数组),当前线程需要自旋等待
            sizeCtl = 0
                表示创建table数组时,使用 DEFAULT_CAPACITY大小

            sizeCtl > 0
            如果table未初始化,表示初始化大小  ,如果调用有参构造器,其中有这么一句 this.sizeCtl = cap;
                                           如果调用无参的那么sizeCtl = 0,走默认长度  -----int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
            如果已经初始化,表示下次扩容时的 触发条件(阈值)
             */
            if ((sc = sizeCtl) < 0)
                Thread.yield(); // lost initialization race; just spin

            // 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")
                        ConcurrentHashMap.Node<K,V>[] nt = (ConcurrentHashMap.Node<K,V>[])new ConcurrentHashMap.Node<?,?>[n];
                        table = tab = nt;
                        // sc = 3/4 * n = 0.75*16 = 12 ; 即为下次扩容的阈值
                        sc = n - (n >>> 2);
                    }
                } finally {
                    /*
                    CASE1  若是第一次初始化,则sizeCtl 是下一次扩容的阈值
                    CASE2  若已经完成初始化,这个线程仍然可以进行CAS,将sizeCtl改成-1,还要把sizeCtl改回去。
                     */
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值