mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-15 10:23:28 +00:00
8202422: value of 'sizeCtl' in ConcurrentHashMap varies with the constructor called
Reviewed-by: martin, psandoz
This commit is contained in:
parent
7ca4027957
commit
abffccb329
@ -702,12 +702,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
* See Hackers Delight, sec 3.2
|
||||
*/
|
||||
private static final int tableSizeFor(int c) {
|
||||
int n = c - 1;
|
||||
n |= n >>> 1;
|
||||
n |= n >>> 2;
|
||||
n |= n >>> 4;
|
||||
n |= n >>> 8;
|
||||
n |= n >>> 16;
|
||||
int n = -1 >>> Integer.numberOfLeadingZeros(c - 1);
|
||||
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
|
||||
}
|
||||
|
||||
@ -844,12 +839,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
* elements is negative
|
||||
*/
|
||||
public ConcurrentHashMap(int initialCapacity) {
|
||||
if (initialCapacity < 0)
|
||||
throw new IllegalArgumentException();
|
||||
int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
|
||||
MAXIMUM_CAPACITY :
|
||||
tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
|
||||
this.sizeCtl = cap;
|
||||
this(initialCapacity, LOAD_FACTOR, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -883,8 +873,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
|
||||
/**
|
||||
* Creates a new, empty map with an initial table size based on
|
||||
* the given number of elements ({@code initialCapacity}), table
|
||||
* density ({@code loadFactor}), and number of concurrently
|
||||
* the given number of elements ({@code initialCapacity}), initial
|
||||
* table density ({@code loadFactor}), and number of concurrently
|
||||
* updating threads ({@code concurrencyLevel}).
|
||||
*
|
||||
* @param initialCapacity the initial capacity. The implementation
|
||||
@ -1473,13 +1463,9 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
||||
if (size == 0L)
|
||||
sizeCtl = 0;
|
||||
else {
|
||||
int n;
|
||||
if (size >= (long)(MAXIMUM_CAPACITY >>> 1))
|
||||
n = MAXIMUM_CAPACITY;
|
||||
else {
|
||||
int sz = (int)size;
|
||||
n = tableSizeFor(sz + (sz >>> 1) + 1);
|
||||
}
|
||||
long ts = (long)(1.0 + size / LOAD_FACTOR);
|
||||
int n = (ts >= (long)MAXIMUM_CAPACITY) ?
|
||||
MAXIMUM_CAPACITY : tableSizeFor((int)ts);
|
||||
@SuppressWarnings("unchecked")
|
||||
Node<K,V>[] tab = (Node<K,V>[])new Node<?,?>[n];
|
||||
int mask = n - 1;
|
||||
|
||||
@ -45,15 +45,20 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Test
|
||||
public class WhiteBox {
|
||||
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
final VarHandle TABLE, NEXTTABLE, SIZECTL;
|
||||
final MethodHandle TABLE_SIZE_FOR;
|
||||
|
||||
WhiteBox() throws ReflectiveOperationException {
|
||||
Class<?> mClass = ConcurrentHashMap.class;
|
||||
@ -65,11 +70,33 @@ public class WhiteBox {
|
||||
TABLE = lookup.findVarHandle(mClass, "table", nodeArrayClass);
|
||||
NEXTTABLE = lookup.findVarHandle(mClass, "nextTable", nodeArrayClass);
|
||||
SIZECTL = lookup.findVarHandle(mClass, "sizeCtl", int.class);
|
||||
TABLE_SIZE_FOR = lookup.findStatic(
|
||||
mClass, "tableSizeFor",
|
||||
MethodType.methodType(int.class, int.class));
|
||||
}
|
||||
|
||||
Object[] table(ConcurrentHashMap m) { return (Object[]) TABLE.getVolatile(m); }
|
||||
Object[] nextTable(ConcurrentHashMap m) { return (Object[]) NEXTTABLE.getVolatile(m); }
|
||||
int sizeCtl(ConcurrentHashMap m) { return (int) SIZECTL.getVolatile(m); }
|
||||
int tableSizeFor(int n) {
|
||||
try {
|
||||
return (int) TABLE_SIZE_FOR.invoke(n);
|
||||
} catch (Throwable t) { throw new AssertionError(t); }
|
||||
}
|
||||
|
||||
List<Supplier<ConcurrentHashMap>> newConcurrentHashMapSuppliers(
|
||||
int initialCapacity) {
|
||||
return List.of(
|
||||
() -> new ConcurrentHashMap(initialCapacity),
|
||||
() -> new ConcurrentHashMap(initialCapacity, 0.75f),
|
||||
() -> new ConcurrentHashMap(initialCapacity, 0.75f, 1));
|
||||
}
|
||||
|
||||
ConcurrentHashMap newConcurrentHashMap(int initialCapacity) {
|
||||
List<Supplier<ConcurrentHashMap>> suppliers
|
||||
= newConcurrentHashMapSuppliers(initialCapacity);
|
||||
return suppliers.get(rnd.nextInt(suppliers.size())).get();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultConstructor() {
|
||||
@ -83,7 +110,7 @@ public class WhiteBox {
|
||||
public void shouldNotResizeWhenInitialCapacityProvided() {
|
||||
int initialCapacity = rnd.nextInt(1, 100);
|
||||
Object[] initialTable = null;
|
||||
ConcurrentHashMap m = new ConcurrentHashMap(initialCapacity);
|
||||
ConcurrentHashMap m = newConcurrentHashMap(initialCapacity);
|
||||
|
||||
// table is lazily initialized
|
||||
assertNull(table(m));
|
||||
@ -101,6 +128,34 @@ public class WhiteBox {
|
||||
assertEquals(initialTable.length, expectedInitialTableLength);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorsShouldGiveSameInitialCapacity() {
|
||||
int initialCapacity = rnd.nextInt(1, 256);
|
||||
long distinctTableLengths
|
||||
= newConcurrentHashMapSuppliers(initialCapacity).stream()
|
||||
.map(Supplier::get)
|
||||
.mapToInt(map -> { map.put(42, 42); return table(map).length; })
|
||||
.distinct()
|
||||
.count();
|
||||
assertEquals(1L, distinctTableLengths);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTableSizeFor() {
|
||||
assertEquals(1, tableSizeFor(0));
|
||||
assertEquals(1, tableSizeFor(1));
|
||||
assertEquals(2, tableSizeFor(2));
|
||||
assertEquals(4, tableSizeFor(3));
|
||||
assertEquals(16, tableSizeFor(15));
|
||||
assertEquals(16, tableSizeFor(16));
|
||||
assertEquals(32, tableSizeFor(17));
|
||||
int maxSize = 1 << 30;
|
||||
assertEquals(maxSize, tableSizeFor(maxSize - 1));
|
||||
assertEquals(maxSize, tableSizeFor(maxSize));
|
||||
assertEquals(maxSize, tableSizeFor(maxSize + 1));
|
||||
assertEquals(maxSize, tableSizeFor(Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
byte[] serialBytes(Object o) {
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
@ -132,7 +187,7 @@ public class WhiteBox {
|
||||
public void testSerialization() {
|
||||
assertInvariants(serialClone(new ConcurrentHashMap()));
|
||||
|
||||
ConcurrentHashMap m = new ConcurrentHashMap(rnd.nextInt(100));
|
||||
ConcurrentHashMap m = newConcurrentHashMap(rnd.nextInt(100));
|
||||
m.put(1, 1);
|
||||
ConcurrentHashMap clone = serialClone(m);
|
||||
assertInvariants(clone);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user