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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
| private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
int n = tab.length, stride;
//根据cpu核心数算出每个线程的扩容数量区间 stride 默认为 16
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
stride = MIN_TRANSFER_STRIDE; // subdivide range
// 说明第一次进入扩容方法 做准备工作
if (nextTab == null) { // initiating
try {
@SuppressWarnings("unchecked")
//创建了一个比扩容之前大一倍的table
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
nextTab = nt;
} catch (Throwable ex) { // try to cope with OOME
sizeCtl = Integer.MAX_VALUE;
return;
}
//新的扩容后的Node[]
nextTable = nextTab;
//标记需要迁移的长度 这是原来的node长度
transferIndex = n;
}
//表示新Node[]的长度
int nextn = nextTab.length;
// 新的node 设置为fwd 节点
ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
//推进标记 用于迁移的时候自循环判断
boolean advance = true;
//完成标记
boolean finishing = false; // to ensure sweep before committing nextTab
// 表示执行到的区间与上限
int i = 0, bound = 0;
for (;;) {
//头结点 与头结点hash
Node<K,V> f; int fh;
// 维护每个线程步长任务区间
while (advance) {
//分配任务的开始下标 nextBound代表长度
int nextIndex, nextBound;
//CASE1:
//成立:表示当前线程的任务尚未完成,还有相应的区间的桶位要处理,--i 就让当前线程处理下一个 桶位.
if (--i >= bound || finishing)
advance = false;
//CASE2: 说明已经分配完毕
else if ((nextIndex = transferIndex) <= 0) {
i = -1;
advance = false;
}
//CASE3:分配任务区间
else if (U.compareAndSwapInt
(this, TRANSFERINDEX, nextIndex,
nextBound = (nextIndex > stride ?
nextIndex - stride : 0))) {
bound = nextBound;
i = nextIndex - 1;
advance = false;
}
}
//CASE1:未分配到任务
if (i < 0 || i >= n || i + n >= nextn) {
int sc;
// 表示扩容完成 使新node 赋值给table
if (finishing) {
nextTable = null;
table = nextTab;
//sizeCtl 恢复 等于扩容阀值
sizeCtl = (n << 1) - (n >>> 1);
return;
}
//未分配到任务 依然要修改SIZECTL -1 代表自己退出了
if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
// 如果分配成功 判断自己是否是最后一个线程 不是就正常退出 是的话就让赋值finishing 在上面条件退出
// 这里其实会检查 会执行到上面while循环做检查 如果有遗漏的还会执行 知道执行到上面条件
if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
return;
finishing = advance = true;
i = n; // recheck before commit
}
}
//CASE2:
//条件成立:说明当前桶位未存放数据,只需要将此处设置为fwd节点即可。
else if ((f = tabAt(tab, i)) == null)
advance = casTabAt(tab, i, null, fwd);
//CASE3:
//条件成立:如果被迁移过 就再次循环
else if ((fh = f.hash) == MOVED)
advance = true; // already processed
//CASE4:
//前置条件:当前桶位有数据,而且node节点 不是 fwd节点,说明这些数据需要迁移。
else {
//sync 加锁当前桶位的头结点
synchronized (f) {
//防止在你加锁头对象之前,当前桶位的头对象被其它写线程修改过,导致你目前加锁对象错误...
if (tabAt(tab, i) == f) {
//ln 表示低位链表引用
//hn 表示高位链表引用
Node<K,V> ln, hn;
//条件成立:表示当前桶位是链表桶位 TREEBIN 是-2
if (fh >= 0) {
//lastRun
//可以获取出 当前链表 末尾连续高位不变的 node
// 先算出头结点hash位置 这时这个n是原数组长度
int runBit = fh & n;
Node<K,V> lastRun = f;
// 这个循环可以拿到一个连续的节点 优化迁移
for (Node<K,V> p = f.next; p != null; p = p.next) {
int b = p.hash & n;
if (b != runBit) {
runBit = b;
lastRun = p;
}
}
//条件成立:说明lastRun引用的链表为 低位链表,那么就让 ln 指向 低位链表
if (runBit == 0) {
ln = lastRun;
hn = null;
}
//否则,说明lastRun引用的链表为 高位链表,就让 hn 指向 高位链表
else {
hn = lastRun;
ln = null;
}
// 遍历到lastRun 节点 说明已经到最后了 因为后面是指向一个地方的
for (Node<K,V> p = f; p != lastRun; p = p.next) {
int ph = p.hash; K pk = p.key; V pv = p.val;
if ((ph & n) == 0)
ln = new Node<K,V>(ph, pk, pv, ln);
else
hn = new Node<K,V>(ph, pk, pv, hn);
}
// 迁移节点
//get 的时候是通过(n - 1) & h 会发现 用原数组 长度 就可以判断出来了
// 新的长度就是原长度左移了一位 所以只需要把这一位加进去就ok
// 重点关注图片中hash后面几位 变的其实就是加了旧数组长度
setTabAt(nextTab, i, ln);
setTabAt(nextTab, i + n, hn);
setTabAt(tab, i, fwd);
advance = true;
}
//条件成立:表示当前桶位是 红黑树 代理结点TreeBin
else if (f instanceof TreeBin) {
//转换头结点为 treeBin引用 t
TreeBin<K,V> t = (TreeBin<K,V>)f;
//低位双向链表 lo 指向低位链表的头 loTail 指向低位链表的尾巴
TreeNode<K,V> lo = null, loTail = null;
//高位双向链表 lo 指向高位链表的头 loTail 指向高位链表的尾巴
TreeNode<K,V> hi = null, hiTail = null;
//lc 表示低位链表元素数量
//hc 表示高位链表元素数量
int lc = 0, hc = 0;
//迭代TreeBin中的双向链表,从头结点 至 尾节点
for (Node<K,V> e = t.first; e != null; e = e.next) {
// h 表示循环处理当前元素的 hash
int h = e.hash;
//使用当前节点 构建出来的 新的 TreeNode
TreeNode<K,V> p = new TreeNode<K,V>
(h, e.key, e.val, null, null);
//条件成立:表示当前循环节点 属于低位链 节点
if ((h & n) == 0) {
//条件成立:说明当前低位链表 还没有数据
if ((p.prev = loTail) == null)
lo = p;
//说明 低位链表已经有数据了,此时当前元素 追加到 低位链表的末尾就行了
else
loTail.next = p;
//将低位链表尾指针指向 p 节点
loTail = p;
++lc;
}
//当前节点 属于 高位链 节点
else {
if ((p.prev = hiTail) == null)
hi = p;
else
hiTail.next = p;
hiTail = p;
++hc;
}
}
ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
(hc != 0) ? new TreeBin<K,V>(lo) : t;
hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
(lc != 0) ? new TreeBin<K,V>(hi) : t;
setTabAt(nextTab, i, ln);
setTabAt(nextTab, i + n, hn);
setTabAt(tab, i, fwd);
advance = true;
}
}
}
}
}
}
// hash 这个原因主要是扩容后的容量实际上是原来容量的二进制位高位多了一个1
// (还有个原因就是get的时候是扩容长度-1 意思就是全是1比如 31==11111 比如15=1111)相当于我们在获取的时候只是高位多了一个1
// 在与运算情况下 比如 10010与10000 会变成10000
// 所以在hash已经确定的情况下 只需要确定高位是否是1 就能确定之后的hash位置
// 比如 你对应的那个高位是0 那么 肯定是原来的位置,如果是1那么就不是原来的位置
|