-
Bug
-
Resolution: Incomplete
-
P4
-
None
-
11
-
x86_64
-
windows_10
ADDITIONAL SYSTEM INFORMATION :
win10, openJDK 11
A DESCRIPTION OF THE PROBLEM :
If we use non-default constructor to new a concurrenthashmap which used ConcurrentHashMap(int initialCapacity) to construct an instance.
I found its inner variable named table might initialized multiple times.
I think this may occur some inknown problem in runtime because table variable should be singleton always.
below is my analysis:
Problem is related to U.compareAndSetInt(this, SIZECTL, sc, -1) //line 2288
Because we use non-default ctor() to instante one map. So its SIZECTL is not default value(such as 64). If one thread cas SIZECTL successfully and another thread also cas successfully in the second turn with table is still null , while will result in table instantiate twice or multiple times.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
step 1: use ConcurrentHashMap(int initialCapacity) to new an instance
step 2: use 10000+ threads to put value. such as map.put("key","value")
---------- BEGIN SOURCE ----------
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
add one mutex to lock the branch. Because map initialize table rapidly, it does not take time to park other thread in concurrent runtime.
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
synchronized(mutex){
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
win10, openJDK 11
A DESCRIPTION OF THE PROBLEM :
If we use non-default constructor to new a concurrenthashmap which used ConcurrentHashMap(int initialCapacity) to construct an instance.
I found its inner variable named table might initialized multiple times.
I think this may occur some inknown problem in runtime because table variable should be singleton always.
below is my analysis:
Problem is related to U.compareAndSetInt(this, SIZECTL, sc, -1) //line 2288
Because we use non-default ctor() to instante one map. So its SIZECTL is not default value(such as 64). If one thread cas SIZECTL successfully and another thread also cas successfully in the second turn with table is still null , while will result in table instantiate twice or multiple times.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
step 1: use ConcurrentHashMap(int initialCapacity) to new an instance
step 2: use 10000+ threads to put value. such as map.put("key","value")
---------- BEGIN SOURCE ----------
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
add one mutex to lock the branch. Because map initialize table rapidly, it does not take time to park other thread in concurrent runtime.
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
synchronized(mutex){
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}