在map中,有个重要的方法,就是put。 多线程环境中,put值的时候,也是最容易出现线程安全的问题。接下来就看看concurrentHashMap中的put方法。 1.put()方法
public V put(K key, V value) {
return putVal(key, value, false);
}
2.接下来主要看看putVal(K key, V value, boolean onlyIfAbsent)方法
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break;
}
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;
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;
}
}
}
addCount(1L, binCount);
return null;
}
2.1) 多线程环境下.在进入该方法中,初始化阶段, k和v 不能为null
if (key == null || value == null) throw new NullPointerException();
2.2)初始化 binCount; binCount 为0 时,node可以直接放着;当为2时,表示当前桶位可能转为红黑树
int binCount = 0;
2.3) 接下来就是for循环的自旋 table 引用map对象的table 在这里,有几个初始化变量
Node<K,V> f; int n, i, fh;
这几个变量的分别作用和含义表示 f:表示桶位的头结点 n: 表示散列表数组的长度 i: 表示key通过寻址计算后,得到的桶位下标 fn: 表示桶位结点的hash值
在这个for循环里面,有4种逻辑作用: CASE 1: 该条件成立,表示 当前map的table未初始化 CASE 2: 表示key使用路由寻址算法得到key对应 table数组的下标位置,tableAt获取指定桶位的头结点,头结点是 null CASE 3:头结点一定不是null, 它的成立条件,表示当前桶位的头结点为FWD节点,表示当前节点正在扩容中。
CASE 4: 当前桶位可能是链表,可能是红黑树 先通过 synchronized 给该条件的进行加锁。 加锁后,又进行 一次对比。避免其他线程将该桶位的头结点修改掉。 在达到满足条件后,又进行了一次for循环的处理, e是每次循环处理的节点。 1.当循环的节点和当前插入节点的key一致时,发生了冲突, 就会进行替换,并跳出该次循环。 2.当前元素和插入元素key不一致时,就会继续走这段程序。 如果满足以下条件,表面是进行红黑树转换的操作
当该次synchronized结束后,会对binCount 不为0的进行一次处理。 将链表的结构转为红黑树。 最后通过addCount()方法,进行统计当前table一共有多少数据,及判断是否达到扩容阈值标准,触发扩容。 具体的该方法,下次再详细描述。
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break;
}
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;
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;
}
}
}
addCount(1L, binCount);
return null;
}
|