1. 初始化
ConcurrentHashMap<String,String> map1 = new ConcurrentHashMap(); // 默认大小16 后初始化 创建时不初始化
ConcurrentHashMap<String,String> map1 = new ConcurrentHashMap(32); // 1.7 那么大小是 32 如果是 1.8则为64
sizeCtl 如果 为0 那么 数组没有初始化 默认 16 如果 是正数 那么未初始化的 记录的是容量 初始化后的记录的是 扩容阈值(数组初始容量 0.75f)
如果 是 -1 表示正在初始化 如果是小于 -1 那么正在扩容 记录的是正在扩容的线程个数 -(1+n)n是线程数量
public ConcurrentHashMap(int initialCapacity) {
// 传入小于0 抛出异常
if (initialCapacity < 0)
throw new IllegalArgumentException();
// 如果我们定义的 长度已经是 比最大值的 一半还要打了 1<<<29次 那么我们集合指定大小为 1<<<30 否则 传入值右移动1位 + 1 之后计算一个比我们传入值 大小 大一倍的 2的 幂次数 如传入 32 计算 16 + 1
int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
MAXIMUM_CAPACITY :
tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
this.sizeCtl = cap;
}
private static final int tableSizeFor(int c) {
int n = c - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
2. putIfAbsent
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
// 不允许 空值 空 key
if (key == null || value == null) throw new NullPointerException();
// 计算hashcode值
int hash = spread(key.hashCode());
int binCount = 0;
// 死循环 便利 哈希数组
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; // 当前数组下标的元素
int n, i, fh; // fh当前元素的 hash值 i 下标 n长度
// 哈希数组是null 或者 长度 = 0 说明没有 初始化 调用 initTable 初始化
if (tab == null || (n = tab.length) == 0)
tab = initTable(); //下方讲解
// 通过路由运算 计算下标 (路由运算 = (长度-1)&hash) // 异位运算 tabAt看下滑
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// 通过cas的方式将数据 添加进哈希数组中
// tab 我现在哈希数组 i 我要插入元素的下标 null 我这个位置是不是null 我要插如的node 元素
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
// 判断我的 hash 是不是 -1 如果是 -1 表示这个下标正在扩容
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
// 对我获取到的节点 加锁
synchronized (f) {
//判断是否 有其他线程 改变 当前位置的元素结构 如 数组 变成 链表
if (tabAt(tab, i) == f) {
// 当前元素的 hash值 是否大于 0 如果大于 0 说明是普通的链表结构
if (fh >= 0) {
binCount = 1;
// 遍历当前节点的 链表
for (Node<K,V> e = f;; ++binCount) {
K ek; // 链表上当前元素的 key
//这里主要判断 我要插入值的位置 已经有内容并且如果和我一致 那么我要替换掉你
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
//如果不一致 那么我 元素等于当前链表
Node<K,V> pred = e;
// 判断我当前链表的下一位是否是null 如果是直接赋值
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
// 判断我这个节点是不是一个树
else if (f instanceof TreeBin) {
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) {
// binCout 大于 2说明是链表 大于等于 8说明链表长度为8 需要树化 如果是2 说明已经是树结果或者是长度为0的链表
// 判断链表长度是否已经超过了 8 为 如果是 我们要 做树化处理
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
// 我们是否已经成功在这个节点添加了 value 没有 就进行下次操作 添加了 跳出循环
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount); // 维护容器长度 判断是否需要扩容
return null;
}
初始化
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
//tab 哈希数组 sc 阈值 或长度 SizeCtl
// 判断 初始化过
while ((tab = table) == null || tab.length == 0) {
// 判断 sizeCtl是不是小于0 小于0 表示已经有线程在初始化 或者 扩容中
if ((sc = sizeCtl) < 0)
// 初始化不需要当前进入的线程 让线程进行 自旋
Thread.yield(); // lost initialization race; just spin
//否则 判断 sizeCtl 是不是 等于 sc 如果等于 把 -1 赋值给 sizeCtl 然后进入初始化
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
// 继续判断 哈希数组是否是null 或者长度 == 0 用来 却别是否 有其他线程已经完成 扩容了
if ((tab = table) == null || tab.length == 0) {
// 判断 sc是否大于0 当 sc大于 0 代表我们 指定了 初始大小 否则长度为默认值 16
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; //创建一个长度为 16的 node数组
//将创建好的 哈希数组 赋值给 table
table = tab = nt;
// 计算 下次扩容的阈值
sc = n - (n >>> 2);
}
} finally {
//指定阈值
sizeCtl = sc;
}
break;
}
}
return tab;
}
tabAt
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
// 通过cas 获取 哈希数组中指定 参数 volatile 指定元素可见性
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
3.扩容机制
addCount
baseCount 表示我当前哈希数组中 一共有多少个元素
private final void addCount(long x, int check) {
CounterCell[] as; long b, s;
// b 临时 baseConut s 是最终 size
// as BASECOUNT自维护数组 用来处理是否有多个线程 竞争BASECOUNT
// 判断我的维护的数组 不是null的 或者 我通过cas竞争修改 失败了
if ((as = counterCells) != null ||
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
// a 是我数组内的 值 m 是 我当前数组 最大下标 v是我数组下标所在的值
CounterCell a; long v; int m;
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
fullAddCount(x, uncontended);
return;
}
// 判断我们当前是不是 链表 不是链表 也没有修改过 结束
if (check <= 1)
return;
// 如果我们的长度有变化 我们的数组内有内容 我们 把数组内的 数值 加和然后和我们 baseCount 相加 得出我们新的长度
s = sumCount();
}
// check大于0 说明我们这个元素的位置有东西
if (check >= 0) {
Node<K,V>[] tab, nt; int n, sc;
// 如果我当前的 长度 大于我的阈值 sizeCtl 并且 我的 哈希数组 不是空的 我的map大小小于 最大大小
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) {
//
int rs = resizeStamp(n);
// 我的sizeiCtl 是否小与0 是 表示 正在扩容
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();
}
}
}
fullAddCount
private final void fullAddCount(long x, boolean wasUncontended) {
int h;
// 给当前线程的 hash 是否更新过了
if ((h = ThreadLocalRandom.getProbe()) == 0) {
ThreadLocalRandom.localInit(); // force initialization
h = ThreadLocalRandom.getProbe();
wasUncontended = true;
}
boolean collide = false; // True if last slot nonempty
for (;;) {
CounterCell[] as; CounterCell a; int n; long v;
// 如果初始化过
if ((as = counterCells) != null && (n = as.length) > 0) {
// 计算数组当前下标的位置是否有值
if ((a = as[(n - 1) & h]) == null){
// 如果没有线程在啊操作
if (cellsBusy == 0) { // Try to attach new Cell
// 创建对象并且赋值
CounterCell r = new CounterCell(x); // Optimistic create
// 如果没人改 并且我通过cas获取到了修改权
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;
}
// true 说明修改成功了 跳出循环 否则结束本次循环
if (created)
break;
continue; // Slot is now non-empty
}
}
collide = false;
}
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
//如果数组对应位置不是null 那么 做 加动作
else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
break;
// 判断数组长度是否 大于cpu核心数
else if (counterCells != as || n >= NCPU)
collide = false; // At max size or stale
else if (!collide)
collide = true;
// 数组 没有人操作 并且我通过 cas获取到了更新 权限
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) {
// 创建一个初始大小是2的数组
CounterCell[] rs = new CounterCell[2];
// 计算下标 //添加 counterCell 对象 x为我们想传入的值 默认 1L
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
}
}