----------------------------------------------------------------------------------------------------------
TheadLocalRandom初始化是把当前线程自己的的种子threadLocalRandomSeed和随机数threadLocalRandomProbe两个属性设置了,getProbe方法其实是用unsafe取当前线程对象中取出随机数的值
-------------------------------------------------------------------------------------------------------
进入fullAddCount方法就意味着并发量很高,cas修改baseCount出现失败的情况了,需要往辅助的储存格里暂存。其实CounterCells就是一个根据并发压力不断2-4-8-16这样扩容的一个数组。不同线程根据随机数hash之后有可能使用同一个格子,所以要有一个全局锁cellsbusy,避免初始化格子数组或者当前格子的时候,并发冲突。ThreadLocalRandom.advanceProbe,同一个格子位并发太高还可以重新生成随机数。
---------------------------------------------------------------------------------------------------------
helpTransfer的核心方法是transfer,在调用之前先做判断(别处调用transfer之前都一样):
一:如果sizeCtl为负,说明此时已经有线程已经开始transfer,那么把当前的sizeCtl+1,这个和ReentrantLock的共享锁是一样的。
二,如果不为负,说明当前线程是第一个开始扩容的,会resizeStamp一下,得到一个保存有tablelength信息的负数(为什么要+2)再替换sizeCtl。
关于sizeCtl:
在transfer中,while(advance)中会判断,如果交替扩容使得transferIndex<=0,说明所有的按stride长度分割的所有区域都已经有线程或正在扩容或已扩容完毕
那么先把自己当前线程的i设置为-1,就可以进入将sizeCtl减一的操作,使得当前线程结束扩容,当前线程退出扩容前,要先通过 sizeCtl减完自己的1之后是否是之前说的resizeStamp右移再加2,.
如果是,说明其他线程都扩容完毕,自己是最后一个线程,那么把newTable属性设置为null,当前table指向newTable,sizeCtl变为新table长度的0.75倍。如果不是,说明还有别的线程在扩容,那么直接返回。
--------------------------------------------------------------------------------------------------------------
stride是并发扩容分区的跨度,table长度不是太大的话一直是16,很大的话会相应扩大。第一个线程A从table最底端开始扩容,下一个线程B(B有可能是A)跨度到tableLength-stride*1的位置开始扩容,下一个线程C跨度到tableLength-stride*2的位置开始扩容。
判断扩容线程数的逻辑在上面,判断从哪个位置开始扩容是在while(advance)中,借助transferIndex 这个变量。分段扩容可以避免并发冲突(冲突发生在比如每次tabAt==null之后的cas为fwdNode,和tabAt!=null的synchronized)
扩容的时候,旧table的链表(或者红黑树)在新table中,被重新hash之后分成两个链表,如果其中有链表长度大于转成红黑树阈值,那么在赋值给新table对应槽位的时候,在红黑树化。换句话说,也就是旧table有旧的链表指向所有的k,v。新链表也有新的两条链表分
别指向原来的k,v。这就是为什么扩容的同时查找并不会受链表一分为二影响。
------------------------------------------------------------------------------------------------------------------
put remove 略