主要分析size()方法、put()方法、addCount()方法
一、部分属性解读
private static final int DEFAULT_CAPACITY = 16;//默认的初始化容量
private static final float LOAD_FACTOR = 0.75f;//默认的负载因子
static final int TREEIFY_THRESHOLD = 8;//链表结构转树结构的阈值
//两个比较重要的节点Hash值
static final int MOVED = -1;//节点的Hash值为-1时(数组某索引下的首节点),表示当前槽位正在进行Rehash
static final int TREEBIN = -2;//树结构根节点的Hash值
transient volatile Node<K,V>[] table;//Hash表中的数组结构,volatile保证原子性和可见性
private transient volatile Node<K,V>[] nextTable;//Rehash时使用的辅助数组,volatile修饰
private transient volatile long baseCount;//用于size()方法,记录在没有线程冲突的情况下,数组大小
private transient volatile int sizeCtl;//目前未知!!
private transient volatile int transferIndex;//目前未知!!
private transient volatile int cellsBusy;//在修改或者新建CounterCells对象状态时的变量锁,1为锁定状态,0为未锁定状态
private transient volatile CounterCell[] counterCells;//CounterCells对象数组,在size()方法中使用,当发生线程冲突的时候,使用该数组进行Map的辅助计数
二、方法解读
1.安全访问数组元素的方法
//获取当前索引上的Node节点
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
//使用CAS操作修改数组上的节点
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
//给数组上的节点赋值
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
2.size() - 并发环境下如何保证size的准确性
使用为size变量加锁,或者使用CAS操作修改size的值也能实现并发环境下对size的修改,但是效率低下。ConcurrentHashMap在线程不冲突和冲突两种情况下对size进行计数
//实际使用sumCount()方法计数
public int size() {
long n = sumCount();
return ((n < 0L) ? 0 :
(n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
(int)n);
}
final long sumCount() {
//记得在第一节中提到的辅助数组吗?
//HashMap总的个数=线程不冲突的情况下baseCount的计数+冲突的情况下CounterCell数组中的计数的和
//在线程冲突的情况下,使用CounterCell数组辅助计数,将有冲突没有叠加在baseCount的数量保存在数组中.
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;
}
3. put() - 如何并发的添加节点
//实际使用putVal方法
public V put(K key, V value) {
return putVal(key, value, false);
}
//关键方法!!
//参数中onlyIfAbsent默认设置为false表示,put一个key时,如果Map中存在这个key,覆盖其value的值
final V putVal(K key, V value, boolean onlyIfAbsent) {
//使用了为什么ConcurrentHashMap不能存key-value为null的键值对
if (key == null || value == null) throw new NullPointerException();
//根据key的hash值计算一个扰动,避免在使用位运算计算数组下表时大量的冲突
int hash = spread(key.hashCode());
//某一个索引下节点的个数
int binCount = 0;
//无限循环-自旋处理
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
//如果put的时候,table还没初始化,先进行初始化,再次进入循环!
if (tab == null || (n = tab.length) == 0)
tab = initTable();
//通过位运算计算数组下标,发现数组当前index的槽位为空,那么使用CAS操作修改该槽位的状态,如果修改成功,退出循环.多线程环境下,如果多个线程同时进行put,其他失败的线程则再次进入循环!
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
//数组中对应index槽位的第一个节点的Hash值为MOVED,参见一,说明这个槽位正在进行Rehash,在Rehash完成后,再次进入循环!
else if ((fh = f.hash) == MOVED)
//参见后面关于该函数的解读!
tab = helpTransfer(tab, f);
else {
//常规情况,即找到index索引下槽位的第一个节点
V oldVal = null;
//ConcurrentHasnMap 细粒度锁的体现,锁加载每个槽位的首节点上,不影响其他槽位节点
//拿不到锁的线程,阻塞
synchronized (f) {
if (tabAt(tab, i) == f) {
//首节点的Hash大于0,参见一,说明此时是链表结构
if (fh >= 0) {
binCount = 1;
//小循环,直到找到一个存在的key或者将新的key添加到Map中为止
for (Node<K,V> e = f;; ++binCount) {
K ek;
//找到一个存在的key,修改key的value,并退出小循环!
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;
//将新的值添加到链表末尾,退出小循环
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) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
//记录size的关键代码!!
addCount(1L, binCount);
return null;
}
addCount() - 如何使用baseCount和counterCells计算size
//check的作用:未知
private final void addCount(long x, int check) {
CounterCell[] as; long b, s;
if ((as = counterCells) != null ||
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
//之前已经发生过线程冲突(才会创建CounterCell数组)或者之前没有冲突,但是CAS修改baseCount的值失败(现在发生了冲突),使用CounterCell数组辅助计数.
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))) {
//如果之前没有发生过冲突,即CounterCell数组为空
//有过冲突,CounterCell数组不为空,但是在计算出的索引上的槽位是空,(该槽位还没有记录)
//槽位有记录的话,就CAS修改槽位的记录值,如果失败了,说明多个线程存在竞争
//fullAddCount参见后一个函数
fullAddCount(x, uncontended);
return;
}
if (check <= 1)
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();
}
}
}
//wasUncontended表示是否存在竞争
//此方法被使用到的情况分为两种:
//第1种是没有竞争的情况,例如,CounterCell数组为空
//第2中是没有竞争的情况,例如,某个槽位的CounterCell对象为空
//第3种是存在竞争的情况,例如,CAS修改CounterCell数组上某个槽位的值失败
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;
//CounterCell数组已经存在
if ((as = counterCells) != null && (n = as.length) > 0) {
//该槽位为空,对应了情况2->为该槽位创建一个CounterCell对象
if ((a = as[(n - 1) & h]) == null) {
//cellsBusy 是一个锁,只有cellsBusy 为0的时候,才能进行下一步操作
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;
//为该槽位生成一个CounterCell对象
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;
}
//成功就退出大循环,表面这一次的addCount()操作完成
//失败就自旋,再次进入循环
if (created)
break;
continue; // Slot is now non-empty
}
}
//collide 值槽位的冲突
collide = false;
}
//CAS修改槽位冲突,自旋,重新进入循环
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
// CAS成功修改了该槽位上CounterCell对象的值
else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
break;
//槽位发生冲突,与HashMap拉链法不同,这里直接进行扩容
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);
}
//CounterCell数组还没有初始化
else if (cellsBusy == 0 && counterCells == as &&
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
boolean init = false;
try { // Initialize table
if (counterCells == as) {
//初始化一个大小为2的CounterCell数组
CounterCell[] rs = new CounterCell[2];
rs[h & 1] = new CounterCell(x);
counterCells = rs;
init = true;
}
} finally {
cellsBusy = 0;
}
if (init)
break;
}
//尝试CAS修改baseCount的值,如果成功就使用BaseCount计数
//个人感觉这一步都走不到
else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
break; // Fall back on using base
}
}