當(dāng)一件事你不能說明白的時(shí)候,十有八九是你還不是十分了解(自勉)
base on JDK 1.8.0_60
首先我們來看看ConcurrentHashMap整體結(jié)構(gòu),如圖所示

ConcurrentHashMap.png
ConcurrentHashMap是如何初始化?
首先我們來看看ConcurrentHashMap關(guān)于初始化比較重要的屬性
/**
* The array of bins. Lazily initialized upon first insertion.
* Size is always a power of two. Accessed directly by iterators.
*/
transient volatile Node<K,V>[] 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中的關(guān)于初始化的代碼initTable()方法
/**
* Initializes table, using the size recorded in sizeCtl.
*/
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
//如果表為空才進(jìn)行初始化工作
while ((tab = table) == null || tab.length == 0) {
//如果sizeCtl小于為負(fù)數(shù)則說明有線程正在進(jìn)行初始化操作
//當(dāng)前線程應(yīng)該放棄CPU的使用
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
//否則說明尚未有線程對表進(jìn)行初始化,那么本線程就來做這個(gè)工作
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
//保險(xiǎn)起見,再次判斷下表是否為空
if ((tab = table) == null || tab.length == 0) {
//至此, sc 大于零說明容量已經(jīng)初始化了,否則使用默認(rèn)容量,其他線程再也無法初始化!!!
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
//構(gòu)建容量數(shù)組
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
//計(jì)算閾值,等效于 n*0.75
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
ConcurrentHashMap是如何統(tǒng)計(jì)元素?cái)?shù)量?
首先我們來看看ConcurrentHashMap關(guān)于元素?cái)?shù)量的輔助屬性與類
/**
* 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()方法,其中主要調(diào)用了sumCount()方法
/**
* {@inheritDoc}
*/
public int size() {
long n = sumCount();
return ((n < 0L) ? 0 :
(n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
(int)n);
}
下面是sunCount()的源碼,可以看出ConcurrentHashMap就是通過計(jì)算baseCount與CountCell數(shù)組的值來計(jì)算ConcurrentHashMap包含的元素的數(shù)量,這就引發(fā)了一個(gè)思考,為什么ConcurrentHashMap要這樣設(shè)計(jì)來統(tǒng)計(jì)包含的元素?cái)?shù)量?
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;
}
未完待續(xù)