当一件事你不能说明白的时候,十有八九是你还不是十分了解(自勉)
base on JDK 1.8.0_60
首先我们来看看ConcurrentHashMap整体结构,如图所示
ConcurrentHashMap是如何初始化?
首先我们来看看ConcurrentHashMap关于初始化比较重要的属性
/**
* The array of bins. Lazily initialized upon first insertion.
* Size is always a power of two. Accessed directly by iterators.
*/
transient volatile Node[] table;
/**
* Table initialization and resizing control. When negative, the
* table is being initialized or resized: -1 for initialization,
* else -(1 + the number of active resizing threads). Otherwise,
* when table is null, holds the initial table size to use upon
* creation, or 0 for default. After initialization, holds the
* next element count value upon which to resize the table.
*/
private transient volatile int sizeCtl;
下面我们来看看ConcurrentHashMap中的关于初始化的代码initTable()方法
/**
* Initializes table, using the size recorded in sizeCtl.
*/
private final Node[] initTable() {
Node[] tab; int sc;
//如果表为空才进行初始化工作
while ((tab = table) == null || tab.length == 0) {
//如果sizeCtl小于为负数则说明有线程正在进行初始化操作
//当前线程应该放弃CPU的使用
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
//否则说明尚未有线程对表进行初始化,那么本线程就来做这个工作
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
//保险起见,再次判断下表是否为空
if ((tab = table) == null || tab.length == 0) {
//至此, sc 大于零说明容量已经初始化了,否则使用默认容量,其他线程再也无法初始化!!!
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
//构建容量数组
Node[] nt = (Node[])new Node,?>[n];
table = tab = nt;
//计算阈值,等效于 n*0.75
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
ConcurrentHashMap是如何统计元素数量?
首先我们来看看ConcurrentHashMap关于元素数量的辅助属性与类
/**
* Base counter value, used mainly when there is no contention,
* but also as a fallback during table initialization
* races. Updated via CAS.
*/
private transient volatile long baseCount;
/**
* A padded cell for distributing counts. Adapted from LongAdder
* and Striped64. See their internal docs for explanation.
*/
@sun.misc.Contended static final class CounterCell {
volatile long value;
CounterCell(long x) { value = x; }
}
下面我们来看看CouncurrentHashMap的size()方法,其中主要调用了sumCount()方法
/**
* {@inheritDoc}
*/
public int size() {
long n = sumCount();
return ((n < 0L) ? 0 :
(n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
(int)n);
}
下面是sunCount()的源码,可以看出ConcurrentHashMap就是通过计算baseCount与CountCell数组的值来计算ConcurrentHashMap包含的元素的数量,这就引发了一个思考,为什么ConcurrentHashMap要这样设计来统计包含的元素数量?
final long sumCount() {
CounterCell[] as = counterCells; CounterCell a;
long sum = baseCount;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
return sum;
}
未完待续