CHM
put方法
1.检查key和value不为null
2.计算hash值
int hash = spread(key.hashCode()); // key.hashCode() = h ,(h ^ (h >>> 16)) & HASH_BITS
3.自旋插入
int binCount = 0; Node<K, V>[] tab = table //将全局table赋值tab
4.初始化table
if (tab == null || (n = tab.length) == 0)
tab = initTable();
private final Node<K, V>[] initTable() {
Node<K, V>[] tab;
int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0) //占位符标识,表示已经有线程正在进行初始化操作
Thread.yield(); // lost initialization race; just spin //放弃时间片,释放cpu
//若没有线程正在进行初始化,则使用CAS操作将sizeCtl设置为-1,表示当前线程正在进行初始化
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
//DEFAULT_CAPACITY 默认容量16
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K, V>[] nt = (Node<K, V>[]) new Node<?, ?>[n]; //创建一个容量为16的数组
table = tab = nt; //奖数组的地址赋值给table
sc = n - (n >>> 2); //sc =12 ,当数组的容量达到12后会进行扩容
}
} finally {
sizeCtl = sc; //当数组的容量达到12后会进行扩容
}
break;
}
}
return tab;
}
5.初始化完成就进行真正的插入操作
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//若tab[i]==null,则代表当前位置还没有值,则不使用锁而是直接进行CAS操作
if (casTabAt(tab, i, null, new Node<K, V>(hash, key, value, null))) break; //操作成功则结束自旋,否则进行下一次循环
}
6.当i位置已经存在元素时,需要进行尾插,返回历史value,没有历史值则返回null
else {
V oldVal = null;
synchronized (f) { //f = tabAt(tab, i = (n - 1) & hash)) f 为Tab[i] 加锁同步
if (tabAt(tab, i) == f) { // tab[i]的位置还没有改变
if (fh >= 0) { //节点正常,代表还是链表结构
binCount = 1; //初始链表的长度为 1 tab[i]
for (Node<K, V> e = f;; ++binCount) { //从头结点开始遍历
K ek;
//int hash = spread(key.hashCode());
//是同一个节点或者key相同
if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent) e.val = value;//如果不是只在缺少的情况下put则会进行value的更新
break;
}
Node<K, V> pred = e; //开始向后遍历
if ((e = e.next) == null) { //没有下一个节点,则直接链接到尾部
pred.next = new Node<K, V>(hash, key, value, null);
break;
}
//e = e.next 在向后遍历的过程中检查有没有key相同的节点,若存在相同的key的节点则替换value值,否则遍历到最后的位置进行尾部插入
}
} else if (f instanceof TreeBin) { //当前的bucket已经转换为红黑树
Node<K, V> p;
binCount = 2;
if ((p = ((TreeBin<K, V>) f).putTreeVal(hash, key, value)) != null) {//插入红黑树,替换或者新增两种情况
oldVal = p.val;
if (!onlyIfAbsent) p.val = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
//当链表长度 >= 8 && tab.length>=64 ,转换成红黑树,否则进行扩容,--》扩容后16->32
treeifyBin(tab, i);
if (oldVal != null) return oldVal;//当oldVal!=null时代表修改,所以直接返回,不需要增加个数
break;
}
}
7.增加节点记数
-
CAS操作baseCount+1
-
CAS操作分片计数数组a的value+1
-
进入fullAddCount(x, uncontended)计数
-
counterCells数组==null,则初始化一个大小为2的数组,并且根据随机数h&1得到一个元素位置,该元素value=1
-
如果存在线程在操作cell,直接CAS操作baseCount+=1
-
countCells已经初始化,元素下标 i=(n - 1) & h,如果该位置未初始化,则进行初始化该元素value=1
-
位置i已经初始化,CAS操作i位置的元素value+1
-
addCount(1L, binCount); //binCount 当前链表的长度 需要判断加上当前元素后需不需要扩容或者树的转换 //1.新增tab[i] ---> binCount = 0 //2.存在一个节点 ---> binCount = 1 //3.存在两个节点 ---> binCount = 2
private final void addCount(long x, int check) { //binCount 当前链表长度
CounterCell[] as; //分片计数
long b, s;
if ((as = counterCells) != null || //已经做过初始化 || CAS设置baseCount失败,直接baseCount+1
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) { //存在线程竞争
CounterCell a; long v; int m;
boolean uncontended = true; //没有竞争
if (as == null || (m = as.length - 1) < 0 ||
//ThreadLocalRandom.getProbe() & m 相当于取模运算 线程安全的随机数生成
(a = as[ThreadLocalRandom.getProbe() & m]) == null || //找到一个分片位置进行累加
//CAS设置value值失败 (如果设置成功 uncontended=true)
!(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
//存在竞争
fullAddCount(x, uncontended);//计数
return;
}
if (check <= 1) //初始化table或者i位置为null或者(fh = f.hash) == MOVED时binCount = 0
return;
s = sumCount();
}
//检查是否需要扩容或者链表转树
if (check >= 0) {
Node<K,V>[] tab, nt; int n, sc;
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) {
int rs = resizeStamp(n);
if (sc < 0) {
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
s = sumCount();
}
}
}
counterCells=null的时候,首先尝试CAS对baseCount进行+1
if ((as = counterCells) != null ||
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
counterCells!=null || (counterCells==null && CAS)失败,往下走
if (as == null || (m = as.length - 1) < 0 ||
(a = as[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
-
counterCells==null 分片为空 uncontended = true
-
(!1)&& counterCells.length - 1 < 0 分片长度为0 uncontended = true
(!2)&&(a=as[ThreadLocalRandom.getProbe() & m]) == null 分片i未初始化 uncontended = true
(!3)&&!U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x) CAS设置i位置的值失败 uncontended = false
进入if代码块
fullAddCount(x, uncontended);//计数 return;
1.counterCells数组==null,则初始化一个大小为2的数组,并且根据随机数h&1得到一个元素位置,该元素value=1
2.如果存在线程在操作cell,直接CAS操作baseCount+=1
3.countCells已经初始化,元素下标 i=(n - 1) & h,如果该位置未初始化,则进行初始化该元素value=1
4.位置i已经初始化,CAS操作i位置的元素value+1
5.位置i已经初始化并且拿到cell操作机会并且数组还没有进行扩容,则对counterCells数组进行2倍扩容,并转移原数组中的元素
// See LongAdder version for explanation
private final void fullAddCount(long x, boolean wasUncontended) {
int h;
if ((h = ThreadLocalRandom.getProbe()) == 0) {
ThreadLocalRandom.localInit(); // force initialization 0x9e3779b9 黄金分割数
h = ThreadLocalRandom.getProbe(); //得到一个随机数,使用黄金分割可以得到较均匀的随机数
wasUncontended = true;//无竞争
}
boolean collide = false; //碰撞 // True if last slot nonempty
for (;;) {
CounterCell[] as; CounterCell a; int n; long v;
//counterCells已经初始化并且长度大于0
if ((as = counterCells) != null && (n = as.length) > 0) {
//(n - 1) & h 相当于取模运算,得到一个位置a
if ((a = as[(n - 1) & h]) == null) { //a的位置还没有值
//Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
if (cellsBusy == 0) { //没有并发
CounterCell r = new CounterCell(x); // 乐观的创建一个CounterCell
if (cellsBusy == 0 &&
//CAS设置有一个线程正在操作 CELLSBUSY
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {//CAS成功进入下面执行
boolean created = false;
try { // Recheck under lock
CounterCell[] rs; int m, j;
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; // Slot is now non-empty
}
}
collide = false;
}
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
break;
else if (counterCells != as || n >= NCPU)
collide = false; // At max size or stale
else if (!collide)
collide = true;
else if (cellsBusy == 0 &&
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
try {
if (counterCells == as) {// Expand table unless stale
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; // Retry with expanded table
}
h = ThreadLocalRandom.advanceProbe(h);
}
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];
rs[h & 1] = new CounterCell(x);
counterCells = rs;
init = true;
}
} finally {
cellsBusy = 0;
}
if (init)
break;
}
else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
break; // Fall back on using base
}
}
1.若位置i还没有初始化并且当前没有线程正在操作,就new 一个CounterCell
2.再次判断没有线程操作CELLSBUSY =0 &&CAS设置当前线程正在操作成功CELLSBUSY 0 -> 1
3.再次判断位置j还没有初始化,就将刚刚new出来的CounterCell放到位置j,否则转5
4.恢复cellsBusy = 0,表示当先线程已经操作完成
if ((a = as[(n - 1) & h]) == null) {
if (cellsBusy == 0) {
CounterCell r = new CounterCell(x); // Optimistic create
if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { //同时只会有一个线程成功
//=================================================================
boolean created = false;
try { // Recheck under lock
CounterCell[] rs; int m, j;
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; // Slot is now non-empty
}
//=================================================================
}
collide = false; 碰撞
}
5.i位置的CounterCell已经初始化,CAS操作 +1
(a = as[(n - 1) & h]) != null //已经初始化 else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x)) //对a做CAS,value+=1 break; //成功则退出方法 h = ThreadLocalRandom.advanceProbe(h); //CAS失败则重新取一个随机值
6.分片数组进行扩容
else if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
try {
if (counterCells == as) {// Expand table unless stale
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; // Retry with expanded table
}
h = ThreadLocalRandom.advanceProbe(h);
7.counterCell==null || length <=0 初始化分片数组,并且将1增加到其中一个元素的value中
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
//h & 1 =0 或者 1
rs[h & 1] = new CounterCell(x); //计数=1
counterCells = rs;
init = true;
}
} finally {
cellsBusy = 0;
}
if (init)
break;
}
8.有竞争,直接CAS操作 baseCount+=1
else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x)) break;
累加分片计数数组的值
final long sumCount() {
CounterCell[] as = counterCells; CounterCell a;
long sum = baseCount;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
return sum;
}
9.判断是否扩容
if (check >= 0) {
Node<K,V>[] tab, nt; int n, sc;
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) { //2的30次幂 1073741824
//Integer.numberOfLeadingZeros(n) | (1 << 15) n的二进制表示的左边0的个数|2的15次幂
//扩容戳 每个容量有一个唯一的扩容戳 32795 ->1000000000011011
//16 - 32795 - 1000000000011011
//32 - 32794 - 1000000000011010
//64 - 32793 - 1000000000011001
int rs = resizeStamp(n);
//-1:表示table正在进行初始化
//>0:表示需要扩容的最大阈值 length*0.75(负载因子)
//<0 && !=-1 : 扩容和同时扩容的线程数
if (sc < 0) {
//sc >>> RESIZE_STAMP_SHIFT) != rs => -1 没有扩容,table初始化
//2149253122l >>> 16 -> 32795 扩容戳
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) //增加一个线程帮助扩容
transfer(tab, nt);
}
//sc = (rs << RESIZE_STAMP_SHIFT) + 2 1000 0000 0001 1011 0000 0000 0000 0010
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2)) //初始时设置扩容线程数2 sc的高位为1代表是负数
transfer(tab, null);
s = sumCount();
}
}
transfer
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE) stride = MIN_TRANSFER_STRIDE; // subdivide 最小步长16 每个线程负责16个长度的迁移
if (nextTab == null) { // initiating CAS操作保证只会有一个线程操作
try {
@SuppressWarnings("unchecked")
Node<K, V>[] nt = (Node<K, V>[]) new Node<?, ?>[n << 1]; //16*2 =32两倍扩容
nextTab = nt;
} catch (Throwable ex) { // try to cope with OOME
sizeCtl = Integer.MAX_VALUE; //扩容出现异常 sizeCtl 由负数 =》 MAX
return;
}
nextTable = nextTab;
transferIndex = n;
}
ForwardingNode<K, V> fwd = new ForwardingNode<K, V>(nextTab);
final Node<K, V>[] nextTable;
ForwardingNode(Node<K, V>[] tab) {
super(MOVED, null, null, null);
this.nextTable = tab;
}
Node(int hash, K key, V val, Node<K, V> next) {
this.hash = hash; // MOVED
this.key = key;
this.val = val;
this.next = next;
}
else if ((f = tabAt(tab, i)) == null)//位置i=null的时候直接在这里放上fwd advance = casTabAt(tab, i, null, fwd);
synchronized (f) { //锁定当前位置i
if (tabAt(tab, i) == f) {
Node<K, V> ln, hn;
if (fh >= 0) { //链表结构
int runBit = fh & n; //11 & 16 =0
Node<K, V> lastRun = f;
for (Node<K, V> p = f.next; p != null; p = p.next) {
int b = p.hash & n; //hash都不相同
if (b != runBit) {
runBit = b;
lastRun = p;
}
}
if (runBit == 0) { //低位链
ln = lastRun;//低位链
hn = null;
} 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)
//this.next=ln 头插法
ln = new Node<K, V>(ph, pk, pv, ln);
else
//this.next=hn 插法
hn = new Node<K, V>(ph, pk, pv, hn);
}
setTabAt(nextTab, i, ln);//低位链设置到新table的i位置
setTabAt(nextTab, i + n, hn);//高位链设置到nextTable的i+n位置
setTabAt(tab, i, fwd); //原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;
}
1.通过数组的方式来实现并发增加元素个数的记录
//counterCells数组,每个CounterCell中保存一个volatile value 保证可见性 //baseCount + 每个counterCell中value的值得到总数 //不同线程不会同时操作同一个counterCell
2.并发扩容,可以通过多个线程来并行实现数据的迁移 扩容戳<<16+线程数
3.采用高低链的方式来解决多次hash计算的问题,提升了效率,只需要CAS迁移两次完成一个位置上的迁移
//低位链放到i位置 //高位链放到i+1位置 //不同线程不会负责相同的区间进行迁移,所以不会有并发问题 16长度扩容32 10001 10000=10000 高位链 1+16 =17 迁移后的位置17 17在16长度的tab中的位置为1 17 10001 15 01111 & 00001=1 17在32长度的tab中的位置为17 31 11111 17 10001 & 10001=17
4.sizeCtl的设计,3种状态表示不同的操作
//-1:表示table正在进行初始化 //>0:表示需要扩容的最大阈值 length*0.75(负载因子) //<0 && !=-1 : 扩容和同时扩容的线程数
5.resizeStamp的设计,高低位的设计来实现当前容量扩容的唯一性以及多个线程的协助扩容的记录
//sc = resizeStamp<<16 + 2 初始 //sc = sc + 1 协助扩容
Java ConcurrentHashMap 源码解析
本文详细解读了ConcurrentHashMap的put方法,包括检查key和value非空、计算hash值、自旋插入、初始化table、插入操作、处理已存在元素、以及节点记数的增加。在增加节点记数时,涉及到了CAS操作、分片计数数组counterCells的初始化和扩容,以及并发控制策略。
1009

被折叠的 条评论
为什么被折叠?



