put()方法
/**
* Implementation for put and putIfAbsent
* onlyIfAbsent为true时,key值已存在则不修改
*/
final V putVal(K key, V value, boolean onlyIfAbsent) {
// key或者value为空则报空指针异常
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
// 循环,只有两个出口([2]、[4])
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
// [1]如果tab = table为空,说明没有初始化,下面进行初始化
if (tab == null || (n = tab.length) == 0)
// 进行初始化数组table
tab = initTable();
// [2]如果已经初始化且该元素所在数组下标位置(n-1二进制全为1,刚好代表数组的大小,所以任意数和它进行与运算都在数组范围之内)上为空则直接插进去
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// 用CAS将该元素插入到数组上
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
// [3]先赋值操作,将f的hash赋值给fh;再判断,如果已经初始化且元素所在数组位置上不为空,判断是否在扩容
// 【hash = MOVED说明,当前正在进行扩容且该位置上节点已经被处理过了】,如果在扩容下面就帮助扩容
// PS:(个人见解:hash != MOVED,当前也可能正在进行扩容,只不过数组当前位置上的元素还没被处理过,
// 所以现在在该位置插入数据不影响后面扩容的时候处理该位置的数据)
else if ((fh = f.hash) == MOVED)
// 帮助进行扩容操作
tab = helpTransfer(tab, f);
// [4]如果已经初始化且对应数组下标位置上不为空,且没有正在进行扩容(或已经在扩容了,只不过当前下标位置还没有进行处理),则进行插入元素
else {
// 存放旧值,put成功以后返回return旧值
V oldVal = null;
// 将数组对应下标处元素(也就是链表头节点或者红黑树的根节点)上锁
synchronized (f) {
// 再次判断该数组下标处元素(后面注释中表达用变量f代替)是否被其他线程修改,如果没有修改则进行插入操作
if (tabAt(tab, i) == f) {
// 如果f位置上是链表结构(红黑树结构fh = -2)
if (fh >= 0) {
// binCount = 链表中与key值相等的节点的长度位置,此时从第一个开始找,先赋值为1
binCount = 1;
// 将f(头节点)赋值给e,遍历链表
for (Node<K,V> e = f;; ++binCount) {
K ek;
// 判断e.key是否等于put进来的key,如果e的key等于put进来的key(也就是等于链表头节点)则修改其value值
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
// 存储key对应的旧值value,用于return
oldVal = e.val;
// onlyIfAbsent为true时,key值已存在则不修改
if (!onlyIfAbsent)
e.val = value;
break;
}
// f.key不等于put进来的key,接着往链表后面找,变量pred存储上一个节点,变量e继续指向链表下一个元素
Node<K,V> pred = e;
// 变量e指向链表下一个元素,看是否为空;
// 不为空则继续循环并将binCount+1,在上面判断此时的e.key是否等于put进来的key;
// 为空说明该key以前不存在,put进来的key为新值,插到末尾
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
// 如果f位置上是红黑树结构
else if (f instanceof TreeBin) {
// 用来指向红黑树中key值对应的节点
Node<K,V> p;
// 因为已经是红黑树了,binCount >= TREEIFY_THRESHOLD为false
binCount = 2;
// 将p指向红黑树里面put进来key值对应的节点,如果该key以前不存在,则将put进来的(key,value)插入红黑树中,p指向null
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
// 存储key对应的旧值value,用于return
oldVal = p.val;
// onlyIfAbsent为true时,key值已存在则不修改
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
// binCount == 0,说明没有进行put操作,可能拿到锁后进行检验f已经被其他线程修改了
if (binCount != 0) {
// 链表长度大于等于8,进化为红黑树
if (binCount >= TREEIFY_THRESHOLD)
// 转化为红黑树
treeifyBin(tab, i);
// onlyIfAbsent为true时,key值已存在则不修改
if (oldVal != null)
return oldVal;
break;
}
}
}
// binCount数值:
// 1)、f为空时:binCount == 0
// 2)、f为链表时:binCount == 链表中与key值相等的节点的长度位置(可能不等于链表长度,已存在情况下可能中间位置就找到了),新增元素时肯定等于链表长度-1(新增链表节点的时候直接break了,没有执行addCount++)
// 3)、f为红黑树时:binCount == 2
addCount(1L, binCount);
return null;
}
transfer()扩容方法
/**
* 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) {
// stride [步幅](每个线程每趟负责处理的长度;例如:数组长度64,stride=16,则每个线程每趟处理16个数组元素,分为4趟去完成扩容)
int n = tab.length, stride;
// NCPU:当前环境下CPU个数
// 如果CPU个数大于1,stride=数组长度无符号右移3位再除以CPU个数,如果CPU个数不大于1,stride=数组长度(只有一个CPU扩容工作只能自己干了)
// 如果stride < MIN_TRANSFER_STRIDE (默认最小转换步幅16),stride = 16
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
stride = MIN_TRANSFER_STRIDE; // subdivide range
// 校验nextTab是否为空,不为空说明已经在进行扩容了
if (nextTab == null) { // initiating
try {
// 新建一个数组,长度为原来的2倍(重复创建问题,不会发生,因为掉用transfer()方法的地方会判断sizeCtl < 0)
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
// 将nextTab指向新数组
nextTab = nt;
} catch (Throwable ex) { // try to cope with OOME
// 出现异常时,将sizeCtl设为Integer.MAX_VALUE
sizeCtl = Integer.MAX_VALUE;
return;
}
nextTable = nextTab;
// 原数组长度n赋值给扩容索引transferIndex
transferIndex = n;
}
// 将新数组长度赋值给nextn
int nextn = nextTab.length;
// 创建ForwardingNode对象,并将新数组存到ForwardingNode中去
ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
// 推进状态
boolean advance = true;
// 扩容是否结束
boolean finishing = false; // to ensure sweep before committing nextTab
// 循环
// i是数组位置索引
// 步幅下边界索引
for (int i = 0, bound = 0;;) {
Node<K,V> f; int fh;
// 循环推进
while (advance) {
// nextIndex:当前transferIndex值(当前步幅的上边界),nextBound:transferIndex需要修改成nextBound
int nextIndex, nextBound;
// 【1】一个步幅扩容未完成(--i >= bound)或者扩容结束(finishing,finishing为true时,i肯定<0)
if (--i >= bound || finishing)
advance = false;
// 【2】将transferIndex赋值给nextIndex,如果将transferIndex赋值给nextIndex<=0,说明数组table所有步幅都已经有线程去处理了,没有待领取的步幅了
else if ((nextIndex = transferIndex) <= 0) {
// 可以结束当前线程的扩容操作了,下面(i < 0 || i >= n || i + n >= nextn)为true
i = -1;
advance = false;
}
// 【3】说明数组table还有步幅没有线程去处理,需要领取该步幅去帮助扩容
// nextBound:如果剩下未同处理的部分长度(transferIndex数值上等于未处理部分长度)不大于步幅[stride],那就默认nextBound = 0(正常最后一个步幅刚好=transferIndex)
// 用CAS去修改transferIndex为nextBound,如果失败说明有其他线程已经领取了这个步幅去处理了
// CAS成功说明成功领取到该步幅([transferIndex - stride,transferIndex - 1])
else if (U.compareAndSwapInt
(this, TRANSFERINDEX, nextIndex,
nextBound = (nextIndex > stride ?
nextIndex - stride : 0))) {
// 将nextBound赋值给bound,方便上面(--i >= bound || finishing)判断当前步幅是否处理完
bound = nextBound;
// 给数组索引赋值(此次步幅扩容范围i[bound,nextIndex - 1])
i = nextIndex - 1;
advance = false;
}
}
// 当前线程已经完成了帮助扩容操作
// i < 0 两种可能:
// 1)、【1】判断条件 --i导致;i减到-1说明数组所有步幅已经全部都有线程去处理了
// 2)、【2】判断条件((nextIndex = transferIndex) <= 0)为true时,执行代码i = -1;transferIndex <= 0说明所有步幅已经全部都有线程去处理了
// 说明已经没有步幅没有线程去处理了,自己的步幅也完成了
if (i < 0 || i >= n || i + n >= nextn) {
int sc;
// 全部扩容工作完成
if (finishing) {
nextTable = null;
table = nextTab;
sizeCtl = (n << 1) - (n >>> 1);
return;
}
// 帮助扩容完成,将sizeCtl - 1,(因为帮助扩容的线程会在帮助扩容之前将sizeCtl + 1)
if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
// (sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT说明还有其他线程未完成扩容操作(因为开始扩容的线程开始前会将sizeCtl设置成(rs << RESIZE_STAMP_SHIFT) + 2)),还未完成扩容就直接退出
if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
return;
// 扩容全部完成
finishing = advance = true;
// 再将i设置成n,完整的遍历一遍原数组,recheck一遍
i = n; // recheck before commit
}
}
// 将tab[i]赋值给f,判断f是否为空,为空则插入ForwardingNode节点占位
else if ((f = tabAt(tab, i)) == null)
advance = casTabAt(tab, i, null, fwd);
// 判断f.hash是否等于MOVED -1(ForwardingNode.hash = MOVED),是说明已经被处理过了(recheck的时候全部走这一个判断)
else if ((fh = f.hash) == MOVED)
advance = true; // already processed
// 没有被处理过的位置(扩容需要处理的位置)
else {
// 给数组元素(链表头节点,红黑树根节点)加锁
synchronized (f) {
// 校验是否被其他线程修改过
if (tabAt(tab, i) == f) {
// 元素在扩容后的数组中的下标要么不变,要么+n(n=原数组长度)
// ln:低位数组元素(低位链表)
// hn:高位数组元素(高位链表)
Node<K,V> ln, hn;
// 链表结构(红黑树结构根节点.hash = -2)
if (fh >= 0) {
// f节点高低位判断条件,fh和n与运算,==1则放在高位,==0则放在低位
// 按照数组下标计算方法,假设n=16,
// 以前的下标位置 == fh & (n-1) == fh & 1111,结果肯定在[0,15];
// 现在的下标位置 == fh & ((n<<2) - 1) == fh & 11111,结果在[0,31],新值和就值是否相同就看高位(第五位),fh第五位为0就和以前结果一样,第五位为1,则比以前大16(也就是n,原数组长度)
// fh & n是否 == 0(fn & 10000),就看高位(第五位是否为0)
int runBit = fh & n;
// 链表最后一个高低位与前面不一致的节点(链表lastRun后面的节点在新数组中的高低位和lastRun一样的),避免了后面不必要的循环挪动链表位置
Node<K,V> lastRun = f;
// 遍历链表,找出链表最后一个高低位与前面不一致的节点(该节点后面的节点在新数组中全部与它高低位相同)
for (Node<K,V> p = f.next; p != null; p = p.next) {
// p节点高低位判断条件,p.hash和n与运算,==1则放在高位,==0则放在低位
int b = p.hash & n;
// f和p或者p和p.next高低位不一样
if (b != runBit) {
// 更新runBit,去当前最新值
runBit = b;
// 推进lastRun,指向当前节点()
lastRun = p;
}
}
// lastRun赋值给低位链表
if (runBit == 0) {
ln = lastRun;
hn = null;
}
// lastRun赋值给高位链表
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);
// 将原数组处理过的位置(tab[i])插入ForwardingNode标识已经被处理过
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;
}
}
}
}
}
}