二 ConcurrentHashMap源码深度解析(java8)——直呼Dou( 七 )

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(ph, pk, pv, ln);elsehn = new Node(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 t = (TreeBin)f;// lo低位树头节点,loTail低位树尾节点// hi高位树头节点,hiTail高位树尾节点TreeNode lo = null, loTail = null;TreeNode hi = null, hiTail = null;int lc = 0, hc = 0;for (Node e = t.first; e != null; e = e.next) {int h = e.hash;TreeNode p = new TreeNode(h, e.key, e.val, null, null);if ((h & n) == 0) {if ((p.prev = loTail) == null)lo = p;elseloTail.next = p;// 尾插法loTail = p;++lc;}else {if ((p.prev = hiTail) == null)hi = p;elsehiTail.next = p;hiTail = p;++hc;}}// 低位节点的个数 <= UNTREEIFY_THRESHOLD=6, 则树退为链表// 否则判断是否有高位节点,无,则原先那棵树t就是一棵低位树,直接赋值给ln// 有高位节点,则低位节点重新树化 。// 高位节点的判断同理ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :(hc != 0) ? new TreeBin(lo) : t;hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :(lc != 0) ? new TreeBin(hi) : t;setTabAt(nextTab, i, ln);setTabAt(nextTab, i + n, hn);setTabAt(tab, i, fwd);advance = true;}}}}}}
那多线程间是如何分配任务的呢?如何将元素从旧数组迁移到新数组的呢?让我再来给你捋捋:
(3.1)多线程间是如何分配任务
首先看开头步长的概念,任务分配单元,就是一次分给线程个位置的元素迁移任务 。只考虑多核的情况计算方式是将原数组的长度分成8份,然后再等分给每个cpu,但是最小步长是=16 。
记录迁移的进度,初始为原数组的长度n,逆序进行 。转发节点,某个位置的元素迁移完了但是整个迁移任务还没结束,这个位置就会被占位,写线程遇到会帮助扩容,读线程遇到转发请求 。
每次线程分配的任务区间为[-, ),每次递减 。源码中是首先会把最新的赋值给,然后-赋值给,同时cas更新为,即分配了一个单位的任务,一个单位任务的区间为[,) 。然而,不是一个线程只分配一个单位的任务,该线程的迁移任务做完了,整个大任务还没有分配完(>0),则还会继续分配,能者多劳 。
在一个[,)单位的任务做迁移时,从i=-1开始降序遍历,直到i--小于bound=说明这个单位的任务做完了,可以分配新的任务了 。当一个位置的节点都迁移完了,旧数组该位置会被fwd占位,同时置为true,表示可以向下一个位置迁移元素了,此时又来到上面的while (){},i--,开始新位置的元素迁移 。
【二ConcurrentHashMap源码深度解析(java8)——直呼Dou】