问题描述
在addCount方法(也包括helpTransfer方法)中,停止容量扩展的第一个条件是(sc >>> RESIZE_STAMP_SHIFT) != rs
,我知道JDK8中存在一个错误:probable bug in logic of ConcurrentHashMap.addCount()。但是在JDK12中,我想知道的是:为什么要删除此条件(sc >>> RESIZE_STAMP_SHIFT) != rs
?我认为这种情况在JDK12中应该变成(sc >>> RESIZE_STAMP_SHIFT) != (rs >>> RESIZE_STAMP_SHIFT)
。
在JDK8中:
private final void addCount(long x,int check) {
//...
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;
//...
}
//...
}
}
}
在JDK12中:
private final void addCount(long x,int check) {
//...
if (check >= 0) {
Node<K,nt;
int n,sc;
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) {
int rs = resizeStamp(n) << RESIZE_STAMP_SHIFT;
if (sc < 0) {
if (sc == rs + MAX_RESIZERS || sc == rs + 1 ||
(nt = nextTable) == null || transferIndex <= 0)
break;
//...
}
//...
}
}
}
我也注意到了Bug in the logic of ConcurrentHashMap.addCount() when used in Threads的问题,但是它仍然处于打开状态
解决方法
(sc >>> RESIZE_STAMP_SHIFT) != rs
表示 tab.length
在赋值 (sc = sizeCtl
) 后发生变化。在这种情况下,必须有另一个线程完成或正在完成调整大小(在传输方法中)。
-
完成后,
nextTable == null
将为true
。 -
当它完成时,
sizeCtl
将与当前的不同,不同的sizeCtl
在不同的调整轮次。
然后 (U.compareAndSetInt(this,SIZECTL,sc,sc + 1)
) 将是假的,下一次迭代中的此线程将中断 sc > 0
的 while 或帮助下一次调整大小或重试。
private final void transfer(Node<K,V>[] tab,Node<K,V>[] nextTab) {
...
if (finishing) {
nextTable = null;
// table changed after "nextTable = null"。
table = nextTab;
sizeCtl = (n << 1) - (n >>> 1);
return;
}
...
}