ConcurrentHashMap的源码分析-fullAddCount源码分析
程序员文章站
2022-03-27 08:46:33
fullAddCount主要是用来初始化CounterCell,来记录元素个数,里面包含扩容,初始化等操作private final void fullAddCount(long x, boolean wasUncontended) {int h; //获取当前线程的probe的值,如果值为0,则初始化当前线程的probe的值,probe就是随机数 if ((h = ThreadLocalRandom.getProbe()) == 0) { ThreadLocalRandom.loc...
fullAddCount主要是用来初始化CounterCell,来记录元素个数,里面包含扩容,初始化等操作
private final void fullAddCount(long x, boolean wasUncontended) {
int h;
//获取当前线程的probe的值,如果值为0,则初始化当前线程的probe的值,probe就是随机数
if ((h = ThreadLocalRandom.getProbe()) == 0) {
ThreadLocalRandom.localInit(); // force initialization
h = ThreadLocalRandom.getProbe();
wasUncontended = true; // 由于重新生成了probe,未冲突标志位设置为true
}
boolean collide = false; // True if last slot nonempty
for (;;) {//自旋
CounterCell[] as; CounterCell a; int n; long v;
// 说明counterCells已经被初始化过了,我们先跳过这个代码,先看初始化部分
if ((as = counterCells) != null && (n = as.length) > 0) {
if ((a = as[(n - 1) & h]) == null) {// 通过该值与当前线程probe求与,获得cells的下标元素,和hash 表获取索引是一样的
if (cellsBusy == 0) { // cellsBusy=0表示counterCells不在初始化或者扩容状态下
CounterCell r = new CounterCell(x); // 构造一个CounterCell的值,传入元素个数
if (cellsBusy == 0 &&// 通过cas设置cellsBusy标识,防止其他线程来对counterCells并发处理
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
boolean created = false;
try { // Recheck under lock
CounterCell[] rs; int m, j;
// 将初始化的r对象的元素个数放在对应下标的位置
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;
continue;//说明指定cells下标位置的数据不为空,则进行下一次循环
}
}
collide = false;
}
//说明在addCount方法中cas失败了,并且获取probe的值不为空
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; //设置为未冲突标识,进入下一次自旋
//由于指定下标位置的cell值不为空,则直接通过cas进行原子累加,如果成功,则直接退出
else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))//
break;
//如果已经有其他线程建立了新的counterCells或者CounterCells大于CPU核心数(很巧妙,线程的并发数不会超过cpu核心数)
else if (counterCells != as || n >= NCPU)
collide = false; //设置当前线程的循环失败不进行扩容
else if (!collide)//恢复collide状态,标识下次循环会进行扩容
collide = true;
//进入这个步骤,说明CounterCell数组容量不够,线程竞争较大,所以先设置一个标识表示为正在扩容
else if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
try {
if (counterCells == as) {// Expand table unless stale
//扩容一倍 2变成4,这个扩容比较简单
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;//继续下一次自旋
}
h = ThreadLocalRandom.advanceProbe(h);//更新随机数的值
}
初始化 CounterCells 数组
//cellsBusy=0表示没有在做初始化,通过cas更新cellsbusy的值标注当前线程正在做初 始化操作
else if (cellsBusy == 0 && counterCells == as && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
boolean init = false;
try { // Initialize table
if (counterCells == as) {
CounterCell[] rs = new CounterCell[2]; //初始化容量为2
rs[h & 1] = new CounterCell(x);//将x也就是元素的个数放在指定的数组 下标位置
counterCells = rs;//赋值给counterCells
init = true;//设置初始化完成标识
}
} finally {
cellsBusy = 0;//恢复标识
}
if (init)
break;
}
//竞争激烈,其它线程占据cell 数组,直接累加在base变量中
else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
break; // Fall back on using base
}
}
本文地址:https://blog.csdn.net/Leon_Jinhai_Sun/article/details/111997777
推荐阅读
-
android的消息处理机制(图文+源码分析)—Looper/Handler/Message
-
go开源项目influxdb-relay源码分析(一)
-
AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)
-
[Abp 源码分析]十、异常处理
-
java视频播放文件(播放器实战和源码分析)
-
java视频播放文件(播放器实战和源码分析)
-
java项目案例分析(免费网站java源码大全)
-
Spring源码分析——调试环境搭建(可能是最省事的构建方法)
-
scrapy-redis源码分析之发送POST请求详解
-
Springboot源码分析之Spring循环依赖揭秘