JDK1.8逐字逐句带你理解ConcurrentHashMap(3)

本文是关于JDK1.8 ConcurrentHashMap的第三篇,重点讲解了扩容(transfer方法)、添加(PUT方法)和查找(GET方法)的核心逻辑。在扩容过程中,涉及nextTable的构建、原table数据的复制与转换;PUT方法中,包括初始化、协助扩容、链表或红黑树的插入规则;GET方法则主要涉及链表和红黑树的查询操作。
摘要由CSDN通过智能技术生成

引言

这篇是介绍ConcurrentHashMap的第三篇,第一篇主要介绍了在jdk1.8中所用到的一些关键知识点,第二篇主要学习了ConcurrentHashMap的组织结构与线程安全的实现,同时介绍了几个极其重要的内部类。这一篇主要是我学习领悟到的几个核心方法,包括扩容,添加和查找。笔者目前整理的一些blog针对面试都是超高频出现的。大家可以点击链接:http://blog.csdn.net/u012403290

transfer方法(扩容方法)
再这之前,我大致描述一下扩容的过程:首先有且只能由一个线程构建一个nextTable,这个nextTable主要是扩容后的数组(容量已经扩大),然后把原table复制到nextTable中,这个过程可以多线程共同操作。但是一定要清楚,这个复制并不是简单的把原table的数据直接移动到nextTable中,而是需要有一定的规律和算法操控的(不然怎么把树转化为链表呢)。

再这之前,先简单说下复制的过程:
数组中(桶中)总共分为3种存储情况:空,链表头,TreeBin头
①遍历原来的数组(原table),如果数组中某个值为空,则直接放置一个forwordingNode(上篇博文介绍过)。
②如果数组中某个值不为空,而是一个链表头结点,那么就对这个链表进行拆分为两个链表,存储到nextTable对应的两个位置。
③如果数组中某个值不为空,而是一个TreeBin头结点,那么这个地方就存储的是红黑树的结构,这样一来,处理就会变得相对比较复杂,就需要先判断需不需要把树转换为链表,做完一系列的处理,然后把对应的结果存储在nextTable的对应两个位置。

在上一篇博文中介绍过,多个线程进行扩容操作的时候,会判断原table的值,如果这个值是forwordingNode就表示这个节点被处理过了,就直接继续往下找。接下来,我们针对源码逐字逐句介绍:

    /**
     * Moves and/or copies the nodes in each bin to new table. See
     * above for explanation.
     */
    private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
        int n = tab.length, stride; //stride 主要和CPU相关
        //主要是判断CPU处理的量,如果小于16则直接赋值16
        if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
            stride = MIN_TRANSFER_STRIDE; // subdivide range
        if (nextTab == null) {            // initiating只能有一个线程进行构造nextTable,如果别的线程进入发现不为空就不用构造nextTable了
            try {
                @SuppressWarnings("unchecked")
                Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1]; //把新的数组变为原来的两倍,这里的n<<1就是向左移动一位,也就是乘以二。
                nextTab = nt;
            } catch (Throwable ex) {      // try to cope with OOME
                sizeCtl = Integer.MAX_VALUE;
                return;
            }
            nextTable = nextTab;
            transferIndex = n; //原先扩容大小
        }
        int nextn = nextTab.length;
        //构造一个ForwardingNode用于多线程之间的共同扩容情况
        ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
        boolean advance = true; //遍历的确认标志
        boolean finishing = false; // to ensure sweep before committing nextTab
        //遍历每个节点
        for (int i = 0, bound = 0;;) {
            Node<K,V> f; int fh; //定义一个节点和一个节点状态判断标志fh
            while (advance) {
                int nextIndex, nextBound;
                if (--i >= bound || finishing)
                    advance = false;
                else if ((nextIndex = transferIndex) <= 0) {
                    i = -1;
                    advance = false;
                }
                //下面就是一个CAS计算
                else if (U.compareAndSwapInt
                         (this, TRANSFERINDEX, nextIndex,
                          nextBound = (nextIndex > stride ?
                                       nextIndex - stride : 0))) {
                    bound = nextBound;
                    i = nextIndex - 1;
                    advance = false;
                }
            }
            if (i < 0 || i >= n || i + n >= nextn) {
                int<
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值