上一 part 说了如何扩容, 接下来讨论不同情况的插入
插入元素到索引位置
1 | if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { |
插入元素时正在扩容
有可能线程 A 进行插入时, 线程 B 正在对数组扩容, 这时候我们需要协助扩容1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 当哈希值是 MOVED, 表示其他线程在扩容, 参考上一 part 扩容部分
if ((fh = f.hash) == MOVED)
// 这个方法放到下面 addCount 方法后面讲
tab = helpTransfer(tab, f);
/**
* ForwardingNode 类
*/
static final class ForwardingNode<K,V> extends Node<K,V> {
// 表示扩容后的新数组
final Node<K,V>[] nextTable;
ForwardingNode(Node<K,V>[] tab) {
super(MOVED, null, null, null);
this.nextTable = tab;
}
}
插入元素到链表或者树
1 | V oldVal = null; |
是否扩容
节点插入后会调用 addCount 方法来判断是否需要扩容1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84private final void addCount(long x, int check) {
CounterCell[] as; long b, s;
// 这里是统计容量 size, 执行加一操作, 不仔细讨论了
if ((as = counterCells) != null ||
!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 ||
(a = as[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
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;
// CAS 把 sizeCtl 成功加一, 本线程开始协助扩容
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
// 没有其他线程扩容, 说明本线程是第一个扩容的
// 此时就把 sizeCtl 设置成一个非常大的负数
// 因为是第一个扩容, 所以新数组是 null
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
s = sumCount();
}
}
}
/**
* 返回一个标识, 这个标识经过 RESIZE_STAMP_SHIFT 左移必定为负数
* Integer.numberOfLeadingZeros 返回 n 对应 32 位二进制数左侧 0 的个数
* 如 9(0000 0000 0000 0000 0000 0000 0000 1001)返回 28
* 1 << (RESIZE_STAMP_BITS - 1) = 2^15,其中 RESIZE_STAMP_BITS = 16
* RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS = 16
*/
static final int resizeStamp(int n) {
return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
}
/**
* 协助扩容方法
* 经过 addCount 方法, 这个协助扩容就更容易理解了
*/
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
Node<K,V>[] nextTab; int sc;
if (tab != null && (f instanceof ForwardingNode) &&
(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
int rs = resizeStamp(tab.length);
// 同样是循环判断是否扩容完成
while (nextTab == nextTable && table == tab &&
(sc = sizeCtl) < 0) {
// 同样是再次判断是否扩容完成
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || transferIndex <= 0)
break;
// 同样是把 sizeCtl 加一, 然后协助扩容
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
transfer(tab, nextTab);
break;
}
}
return nextTab;
}
return table;
}
如果有疑问欢迎来 Issues 探讨