引言
这篇是介绍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<