美文网首页
04 ConcurrentHashMap1.8源码深入剖析

04 ConcurrentHashMap1.8源码深入剖析

作者: 攻城老狮 | 来源:发表于2022-09-06 15:07 被阅读0次

    1 ConcurrentHashMap 的构造方法

    /**
    * 默认的构造方法为空,不做任何操作,数组长度默认是16
    */
    public ConcurrentHashMap() {
    }
    
    /**
    * 传递初始化容量的构造方法,传递进来一个初始容量,
    * ConcurrentHashMap会基于这个值计算一个比这个值大的2的幂次方数作为初始容量
    * 与其他版本不同,例如:传递 16 作为参数,它会计算得到 32 作为初始化容量,而不是 16
    */
    public ConcurrentHashMap(int initialCapacity) {
      if (initialCapacity < 0)
        throw new IllegalArgumentException();
      int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
                 MAXIMUM_CAPACITY :
                 tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
      this.sizeCtl = cap;
    }
    

    2 添加元素

    2.1 sizeCtl 含义解析

    这个变量是一个非常重要的变量,而且具有非常丰富的含义,它的值不同,对应的含义也不一样

    1.sizeCtl为0,代表数组未初始化, 且数组的初始容量为16
    2.sizeCtl为正数,如果数组未初始化,那么其记录的是数组的初始容量,如果数组已经初始化,那么其记录的是数组的扩容阈值
    3.sizeCtl为-1,表示数组正在进行初始化
    4.sizeCtl小于0,并且不是-1,表示数组正在扩容, -(1+n),表示此时有n个线程正在共同完成数组的扩容操作
    

    2.2 put() 方法

    /**
    * put() 方法会默认调用 putVal() 方法做具体添加元素逻辑
    */
    public V put(K key, V value) {
      return putVal(key, value, false);
    }
    

    2.3 putval() 方法

    /**
    * putVal() 方法做具体添加元素逻辑。根据不同的条件进行不同的插入方案选择。
    * 大致分为 直接cas插入,链表插入,树插入以及协助扩容等操作。
    * 添加完毕后需要对数组元素个数更新,并且判别是否需要扩容。
    */
    final V putVal(K key, V value, boolean onlyIfAbsent) {
      // 不接受 null 值,直接抛出空指针异常
      if (key == null || value == null) throw new NullPointerException();
      // 计算 key 的 hash 值
      int hash = spread(key.hashCode());
      // 记录某个桶上元素的个数,如果超过8个,会转成红黑树
      int binCount = 0;
      for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        // 1.如果数组为空,表示未进行初始化,需要首先进行初始化操作
        if (tab == null || (n = tab.length) == 0)
          tab = initTable();
        // 2.如果 hash 计算得到的桶位置没有元素,利用cas将元素直接添加
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
          if (casTabAt(tab, i, null,
                       new Node<K,V>(hash, key, value, null)))
            break;                   // no lock when adding to empty bin
        }
        // 3.如果hash计算得到的桶位置元素的hash值为MOVED,证明其他线程正在扩容,那么需要协助扩容
        else if ((fh = f.hash) == MOVED)
          tab = helpTransfer(tab, f);
        // 4.hash 计算的桶位置元素不为空,且当前没有处于扩容操作,进行元素添加
        else {
          V oldVal = null;
          // 对当前桶进行加锁,保证线程安全,执行元素添加操作
          // 普通链表 : 尾插法
          // 树化后 : 插入树节点
          // 加锁 为节点头元素添加重量级锁
          synchronized (f) {
            // dcl double check 如果其他线程已经修改头节点,则可以感知
            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;
                }
              }
            }
          }
          if (binCount != 0) {
            // 链表长度大于/等于8,将链表转成红黑树(这个8设计到泊松分布)
            if (binCount >= TREEIFY_THRESHOLD)
              // 树化,因为树化还有满足数组长度是否超过 64
              treeifyBin(tab, i);
            // 如果是重复键,直接将旧值返回
            if (oldVal != null)
              return oldVal;
            break;
          }
        }
      }
      // 添加的是新元素,维护集合长度,并判断是否要进行扩容操作
      addCount(1L, binCount);
      return null;
    }
    

    2.4 initTable() 方法

    /**
    * initTable()方法采用 CAS+自旋的方式线程安全的初始化数组
    */
    private final Node<K,V>[] initTable() {
      Node<K,V>[] tab; int sc;
      // cas+自旋,保证线程安全,对数组进行初始化操作
      while ((tab = table) == null || tab.length == 0) {
        // 说明其他线程正在进行初始化,本线程让出 CPU
        if ((sc = sizeCtl) < 0)
          Thread.yield(); // lost initialization race; just spin
        // cas修改sizeCtl的值为-1,修改成功,进行数组初始化,失败,继续自旋
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
          try {
            // double check
            if ((tab = table) == null || tab.length == 0) {
              // sizeCtl为0,取默认长度16,否则取用户指定的sizeCtl值
              int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
              @SuppressWarnings("unchecked")
              // 基于初始长度,构建数组对象
              Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
              table = tab = nt;
              // 计算扩容阈值,并赋值给sc,0.75
              sc = n - (n >>> 2);
            }
          } finally {
            // 将扩容阈值,赋值给sizeCtl
            sizeCtl = sc;
          }
          break;
        }
      }
      return tab;
    }
    

    3 链表树化

    3.1 treeifyBin() 方法

    /**
    * treeifyBin()方法。首先对数组长度判别,查看是否直接扩容数组即可。
    * 之后若满足树化条件,则进行树化的步骤,进行双向链表构建,并进行 TreeBin 对象的构建。
    */
    private final void treeifyBin(Node<K,V>[] tab, int index) {
      Node<K,V> b; int n, sc;
      if (tab != null) {
        // 判别当前数组长度是否超过 64(树化阈值),如果不超过阈值则进行数组的扩容
        // static final int MIN_TREEIFY_CAPACITY = 64;
        if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
          tryPresize(n << 1);
        // 再次核实当前桶存在元素,并且是链表
        else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
          // 并发安全的进行树化
          synchronized (b) {
            // double check
            if (tabAt(tab, index) == b) {
              // 首先进行双向链表的构造
              TreeNode<K,V> hd = null, tl = null;
              for (Node<K,V> e = b; e != null; e = e.next) {
                TreeNode<K,V> p =
                  new TreeNode<K,V>(e.hash, e.key, e.val,
                                    null, null);
                if ((p.prev = tl) == null)
                  hd = p;
                else
                  tl.next = p;
                tl = p;
              }
              // 将树化后的 TreeBin 对象插入到桶中。具体的树化逻辑与HashMap不同,将TreeNode封装到 TreeBin 对象中,方便平衡树的过程中保证桶中的对象不发生变化。便于对first元素进行加锁操作
              setTabAt(tab, index, new TreeBin<K,V>(hd));
            }
          }
        }
      }
    }
    

    3.2 TreeBin 初始化

    /**
    * TreeBin 的构造方法为具体构建红黑树的逻辑。
    */
    TreeBin(TreeNode<K,V> b) {
      super(TREEBIN, null, null, null);
      this.first = b;
      TreeNode<K,V> r = null;
      // 遍历双向链表
      for (TreeNode<K,V> x = b, next; x != null; x = next) {
        next = (TreeNode<K,V>)x.next;
        x.left = x.right = null;
        // 构建红黑树的root节点
        if (r == null) {
          x.parent = null;
          x.red = false;
          r = x;
        }
        // 构建红黑树具体逻辑
        else {
          // 获取元素的 key 和 hash
          K k = x.key;
          int h = x.hash;
          Class<?> kc = null;
          // 遍历红黑树
          for (TreeNode<K,V> p = r;;) {
            int dir, ph;
            K pk = p.key;
            // 向左遍历
            if ((ph = p.hash) > h)
              dir = -1;
            // 向右遍历
            else if (ph < h)
              dir = 1;
            // 根据是否实现 comparable 接口的逻辑判别左右遍历
            else if ((kc == null &&
                      (kc = comparableClassFor(k)) == null) ||
                     (dir = compareComparables(kc, k, pk)) == 0)
              dir = tieBreakOrder(k, pk);
            // 记录父节点
            TreeNode<K,V> xp = p;
            // 构建新节点
            if ((p = (dir <= 0) ? p.left : p.right) == null) {
              x.parent = xp;
              if (dir <= 0)
                xp.left = x;
              else
                xp.right = x;
              // 平衡红黑树
              r = balanceInsertion(r, x);
              break;
            }
          }
        }
      }
      this.root = r;
      assert checkInvariants(root);
    }
    

    4 计数操作

    jdk1.8中使用了一个仿造LongAdder实现的计数器,让计数操作额外使用别的基于分段并发思想的实现的类。

    4.1 addCount() 方法

    /**
    * addCount() 方法主要包括两个功能:
    * 1.记录ConcurrentHashMap元素数量,会调用fullAddCount具体执行
    * 2.扩容ConcurrentHashMap ,会调用transer方法具体执行扩容
    */
    private final void addCount(long x, int check) {
      CounterCell[] as; long b, s;
      // 当CounterCell数组不为空,则优先利用数组中的CounterCell记录数量
      // 或者当baseCount的累加操作失败,会利用数组中的CounterCell记录数量
      if ((as = counterCells) != null ||
          !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
        CounterCell a; long v; int m;
        // 标识是否有多线程竞争
        boolean uncontended = true;
        // 当as数组为空
        // 或者当as长度小于0
        // 或者当前线程对应的as数组桶位的元素为空
        // 或者当前线程对应的as数组桶位不为空,但是累加失败
        if (as == null || (m = as.length - 1) < 0 ||
            (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
            !(uncontended =
              U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
          // 以上任何一种情况成立,都会进入该方法,传入的uncontended是false
          fullAddCount(x, uncontended);
          return;
        }
        if (check <= 1)
          return;
        // 计算元素个数
        s = sumCount();
      }
      // 接着判断是否需要扩容
      if (check >= 0) {
        Node<K,V>[] tab, nt; int n, sc;
        // 当元素个数达到扩容阈值
        // 并且数组不为空
        // 并且数组长度小于限定的最大值
        // 满足以上所有条件,执行扩容
        while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
               (n = tab.length) < MAXIMUM_CAPACITY) {
          // 这个是一个很大的正数
          int rs = resizeStamp(n);
          // sc小于0,说明有线程正在扩容,那么会协助扩容
          if (sc < 0) {
            // 扩容结束或者扩容线程数达到最大值或者扩容后的数组为null或者没有更多的桶位需要转移,结束操作
            if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                transferIndex <= 0)
              break;
            // 扩容线程加1,成功后,进行协助扩容操作
            if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
              // 协助扩容,newTable不为null
              transfer(tab, nt);
          }
          // 没有其他线程在进行扩容,达到扩容阈值后,给sizeCtl赋了一个很大的负数
          else if (U.compareAndSwapInt(this, SIZECTL, sc,
                                       (rs << RESIZE_STAMP_SHIFT) + 2))
            // 扩容,newTable为null
            transfer(tab, null);
          s = sumCount();
        }
      }
    }
    

    4.2 fullAddCount() 方法

    /**
    * fullAddCount() 方法用于将需要添加的个数累加到baseCount上,
    * 或者累加到其他CountCell数组中的每个对象中的value属性上
    */
    private final void fullAddCount(long x, boolean wasUncontended) {
      int h;
      // 获取当前线程的hash值,如果为 0,则重新进行 hash 值计算
      if ((h = ThreadLocalRandom.getProbe()) == 0) {
        ThreadLocalRandom.localInit();      // force initialization
        h = ThreadLocalRandom.getProbe();
        wasUncontended = true;
      }
      boolean collide = false;                // True if last slot nonempty
      for (;;) {
        CounterCell[] as; CounterCell a; int n; long v;
        // 数组不为空,优先对数组中CouterCell的value累加
        if ((as = counterCells) != null && (n = as.length) > 0) {
          // 线程对应的桶为 null
          if ((a = as[(n - 1) & h]) == null) {
            if (cellsBusy == 0) {            // Try to attach new Cell
              // 创建CounterCell对象
              CounterCell r = new CounterCell(x); // Optimistic create
              // 利用CAS修改cellBusy状态为1,成功则将创建的CounterCell对象放入数组中
              if (cellsBusy == 0 &&
                  U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                boolean created = false;
                try {               // Recheck under lock
                  CounterCell[] rs; int m, j;
                  // double check
                  if ((rs = counterCells) != null &&
                      (m = rs.length) > 0 &&
                      rs[j = (m - 1) & h] == null) {
                    rs[j] = r;
                    // 表示放入成功
                    created = true;
                  }
                } finally {
                  // 重置标志位
                  cellsBusy = 0;
                }
                // 成功退出循环
                if (created)
                  break
                // 桶已经被别的线程放置了 CounterCell 对象,继续循环
                continue;           // Slot is now non-empty
              }
            }
            collide = false;
          }
          // 桶不为空,重新计算线程hash值,然后继续循环
          else if (!wasUncontended)       // CAS already known to fail
            wasUncontended = true;      // Continue after rehash
          // 重新计算了hash值后,对应的桶位依然不为空,对value累加
          // 成功则结束循环
          // 失败则继续下面判断
          else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
            break;
          // 数组被别的线程改变了,或者数组长度超过了可用cpu大小,重新计算线程hash值,否则继续下一个判断
          else if (counterCells != as || n >= NCPU)
            collide = false;            // At max size or stale
          // 当没有冲突,修改为有冲突,并重新计算线程hash,继续循环
          else if (!collide)
            collide = true;
          // 如果CounterCell的数组长度没有超过cpu核数,对数组进行两倍扩容
          else if (cellsBusy == 0 &&
                   U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
            try {
              // double check
              if (counterCells == as) {// Expand table unless stale
                // 对 CounterCell 数组进行2倍扩容
                CounterCell[] rs = new CounterCell[n << 1];
                for (int i = 0; i < n; ++i)
                  rs[i] = as[i];
                counterCells = rs;
              }
            } finally {
              cellsBusy = 0;
            }
            collide = false;
            continue;                   // Retry with expanded table
          }
          // 重新计算 hash 值
          h = ThreadLocalRandom.advanceProbe(h);
        }
        // CounterCell数组为空,并且没有线程在创建数组,修改标记,并创建数组
        else if (cellsBusy == 0 && counterCells == as &&
                 U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
          boolean init = false;
          try {                           // Initialize table
            // double check
            if (counterCells == as) {
              // 初始化容量为2的数组
              CounterCell[] rs = new CounterCell[2];
              // 放入 count 计数
              rs[h & 1] = new CounterCell(x);
              counterCells = rs;
              init = true;
            }
          } finally {
            cellsBusy = 0;
          }
          if (init)
            break;
        }
        // 数组为空,并且有别的线程在创建数组,那么尝试对baseCount做累加,成功就退出循环,失败就继续循环
        else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
          break;                          // Fall back on using base
      }
    }
    

    4.3 sumCount() 方法

    /**
    * sumCount() 方法会聚合所有累加单元和baseCount的值
    */
    final long sumCount() {
      CounterCell[] as = counterCells; CounterCell a;
      // 获取baseCount的值
      long sum = baseCount;
      if (as != null) {
        // 遍历CounterCell数组,累加每一个CounterCell的value值
        for (int i = 0; i < as.length; ++i) {
          if ((a = as[i]) != null)
            sum += a.value;
        }
      }
      return sum;
    }
    

    5 扩容操作

    多线程协助扩容的操作会在两个地方被触发:

    ① 当添加元素时,发现添加的元素对用的桶位为fwd节点,就会先去协助扩容,然后再添加元素。

    ② 当添加完元素后,判断当前元素个数达到了扩容阈值,此时发现sizeCtl的值小于0,并且新数组不为空,这个时候,会去协助扩容。

    5.1 transfer() 方法

    /**
    * ConcurrentHashMap触发扩容的时机与HashMap类似,
    * 要么是在将链表转换成红黑树时判断table数组的长度是否小于阈值(64),
    * 如果小于就进行扩容而不是树化,要么就是在添加元素的时候,
    * 判断当前Entry数量是否超过阈值,如果超过就进行扩容。
    */
    private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
      int n = tab.length, stride;
      // 如果是多cpu,那么每个线程划分任务,最小任务量是16个桶位的迁移
      if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
        stride = MIN_TRANSFER_STRIDE; // subdivide range
      // 如果是扩容线程,此时新数组为null
      if (nextTab == null) {            // initiating
        try {
          @SuppressWarnings("unchecked")
          // 两倍扩容创建新数组
          Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[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;
      // 已迁移的桶,会用这个对象节点占位(这个节点的hash值为-1--MOVED)
      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;
        while (advance) {
          int nextIndex, nextBound;
          // i记录当前正在迁移桶位的索引值
          // bound记录下一次任务迁移的开始桶位
          // --i >= bound 成立表示当前线程分配的迁移任务还没有完成
          if (--i >= bound || finishing)
            advance = false;
          // 没有元素需要迁移 后续会去将扩容线程数减1,并判断扩容是否完成
          else if ((nextIndex = transferIndex) <= 0) {
            i = -1;
            advance = false;
          }
          // 计算下一次任务迁移的开始桶位置,并将这个值赋值给transferIndex
          else if (U.compareAndSwapInt
                   (this, TRANSFERINDEX, nextIndex,
                    nextBound = (nextIndex > stride ?
                                 nextIndex - stride : 0))) {
            bound = nextBound;
            i = nextIndex - 1;
            advance = false;
          }
        } 
        // 如果没有更多的需要迁移的桶位,就进入该if
        if (i < 0 || i >= n || i + n >= nextn) {
          int sc;
          // 扩容结束后,保存新数组,并重新计算扩容阈值,赋值给sizeCtl
          if (finishing) {
            nextTable = null;
            table = nextTab;
            sizeCtl = (n << 1) - (n >>> 1);
            return;
          }
          // 扩容任务线程数减1
          if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
            // 判断当前所有扩容任务线程是否都执行完成
            if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
              return;
            // 所有扩容线程都执行完,标识结束
            finishing = advance = true;
            i = n; // recheck before commit
          }
        }
        // 当前迁移的桶位没有元素,直接在该位置添加一个fwd节点
        else if ((f = tabAt(tab, i)) == null)
          advance = casTabAt(tab, i, null, fwd);
        // 当前节点已经被迁移
        else if ((fh = f.hash) == MOVED)
          advance = true; // already processed
        // 当前节点需要迁移,加锁迁移,保证多线程安全
        // 此处迁移逻辑和jdk7的ConcurrentHashMap相同,不再赘述
        else {
          synchronized (f) {
            if (tabAt(tab, i) == f) {
              Node<K,V> ln, hn;
              if (fh >= 0) {
                int runBit = fh & n;
                Node<K,V> lastRun = f;
                for (Node<K,V> p = f.next; p != null; p = p.next) {
                  int b = p.hash & n;
                  if (b != runBit) {
                    runBit = b;
                    lastRun = p;
                  }
                }
                if (runBit == 0) {
                  ln = lastRun;
                  hn = null;
                }
                else {
                  hn = lastRun;
                  ln = null;
                }
                for (Node<K,V> p = f; p != lastRun; p = p.next) {
                  int ph = p.hash; K pk = p.key; V pv = p.val;
                  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);
                setTabAt(nextTab, i + n, hn);
                setTabAt(tab, i, fwd);
                advance = true;
              }
              else if (f instanceof TreeBin) {
                TreeBin<K,V> t = (TreeBin<K,V>)f;
                TreeNode<K,V> lo = null, loTail = null;
                TreeNode<K,V> hi = null, hiTail = null;
                int lc = 0, hc = 0;
                for (Node<K,V> e = t.first; e != null; e = e.next) {
                  int h = e.hash;
                  TreeNode<K,V> p = new TreeNode<K,V>
                    (h, e.key, e.val, null, null);
                  if ((h & n) == 0) {
                    if ((p.prev = loTail) == null)
                      lo = p;
                    else
                      loTail.next = p;
                    loTail = p;
                    ++lc;
                  }
                  else {
                    if ((p.prev = hiTail) == null)
                      hi = p;
                    else
                      hiTail.next = p;
                    hiTail = p;
                    ++hc;
                  }
                }
                ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
                (hc != 0) ? new TreeBin<K,V>(lo) : t;
                hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
                (lc != 0) ? new TreeBin<K,V>(hi) : t;
                setTabAt(nextTab, i, ln);
                setTabAt(nextTab, i + n, hn);
                setTabAt(tab, i, fwd);
                advance = true;
              }
            }
          }
        }
      }
    }
    

    5.2 helpTransfer() 方法

    /**
    * helpTransfer() 方法是用于其他线程协助扩容的功能。
    */
    final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
      Node<K,V>[] nextTab; int sc;
      // 当数组不为空并且fwd为占位对象并且扩容的新数组存在时,需要进行协助扩容
      if (tab != null && (f instanceof ForwardingNode) &&
          (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
        int rs = resizeStamp(tab.length);
        // CAS+自旋
        while (nextTab == nextTable && table == tab &&
               (sc = sizeCtl) < 0) {
          // 扩容结束或者扩容线程数达到最大值或者扩容后的数组为null或者没有更多的桶位需要转移,结束操作
          if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
              sc == rs + MAX_RESIZERS || transferIndex <= 0)
            break;
          // 扩容线程加1,成功后,进行协助扩容操作
          if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
            // 扩容,传递一个不是null的nextTab
            transfer(tab, nextTab);
            break;
          }
        }
        return nextTab;
      }
      return table;
    }
    

    相关文章

      网友评论

          本文标题:04 ConcurrentHashMap1.8源码深入剖析

          本文链接:https://www.haomeiwen.com/subject/ggygnrtx.html