初始化table:
/**
* 初始化table
* @return
*/
private final ConcurrentHashMap.Node<K,V>[] initTable() {
ConcurrentHashMap.Node<K,V>[] tab; int sc;
/**
* 死循环判断 table是否为null,或者长度为0
*/
while ((tab = table) == null || tab.length == 0) {
/**
* 刚初始化sizeCtl=0,如果小于0说明有线程已经在初始化table
*/
if ((sc = sizeCtl) < 0)
/**
* 当前线程让步
*/
Thread.yield(); // lost initialization race; just spin
/**
* 使用cas操作,将sizeCtl的值设置为-1,设置成功返回ture,否则返回false
*/
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
/**
* 再次校验table
*/
if ((tab = table) == null || tab.length == 0) {
/**
* DEFAULT_CAPACITY 默认长度是16
*/
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
/**
* 常见长度为n的Node节点
*/
@SuppressWarnings("unchecked")
ConcurrentHashMap.Node<K,V>[] nt = (ConcurrentHashMap.Node<K,V>[])new ConcurrentHashMap.Node<?,?>[n];
table = tab = nt;
/**
* 默认为12
*/
sc = n - (n >>> 2);
}
} finally {
/**
* 赋值sizeCtl
*/
sizeCtl = sc;
}
break;
}
}
return tab;
}
put方法:
// boolean onlyIfAbsent 参数默认是false,表示key相同时,是否更新旧值 false表示更新旧值,true表示不更新旧值
final V putVal(K key, V value, boolean onlyIfAbsent) {
// key或者value是null,抛出空指针异常
if (key == null || value == null) throw new NullPointerException();
// 获取key的hash值,spread内部 (h ^ (h >>> 16)) & HASH_BITS
int hash = spread(key.hashCode());
// 这个是记录当前链表元素个数
int binCount = 0;
// 死循环
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
// 第一次循环,此时的table、tab为null
if (tab == null || (n = tab.length) == 0)
// 初始化table对象,默认长度是16
tab = initTable();
// 循环,跟据(n - 1) & hash定位放入元素位置,如果当前索引没有元素,这个位置返回的是null,有元素返回的第一个元素
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// 通过cas操作,将这个元素放入i的位置,如果成功,结束循环
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
//
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
// 在某个索引上面添加元素,链表或者红黑树
else {
V oldVal = null;
// 最小力度加锁,只加锁某个索引
synchronized (f) {
if (tabAt(tab, i) == f) {
// 链表
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
// 判断当前要put的值key是否已经存在
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
// key已经存在
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)
// 在转为红黑树前会怕断,当前元素个数是否超过64,超过64才会转为红黑树
reeifyBin(tab, i);
// 是否是更新的旧值,是直接返回旧值,结束
if (oldVal != null)
return oldVal;
break;
}
}
}
//
addCount(1L, binCount);
return null;
}
本文详细解析了ConcurrentHashMap的初始化table过程及put方法,涉及cas操作、table定位、链表与红黑树的处理。
1397

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



