ConcurrentHashMap設計很強,其中sizeCtl設計十分巧妙。但是在網上查資料真的是以訛傳訛啊。所以,我來寫一下sizeCtl的說明。
sizeCtl有多重含義,其中除了擴容的時候難理解外,其他的比較好理解
· 1 如果一個ConcurrentHashMap正在初始化,值為-1
2 ConcurrentHashMap初始化完成正在使用,置為size * 0.75
3 擴容的時候,是一個負值
我們今天就來看看這個擴容的時候的值是怎么來的
private final void tryPresize(int size) { int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor(size + (size >>> 1) + 1); int sc; while ((sc = sizeCtl) >= 0) { Node<K,V>[] tab = table; int n; if (tab == null || (n = tab.length) == 0) { n = (sc > c) ? sc : c; if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { try { if (table == tab) { @SuppressWarnings("unchecked") Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; table = nt; sc = n - (n >>> 2); } } finally { sizeCtl = sc; } } } else if (c <= sc || n >= MAXIMUM_CAPACITY) break; else if (tab == table) { int rs = resizeStamp(n); if (sc < 0) { Node<K,V>[] nt; if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || (nt = nextTable) == null || transferIndex <= 0) break; if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) transfer(tab, nt); } else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)) transfer(tab, null); } } }
如果第一個線程觸發了擴容,那么注意這句話
U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)
rs 的值是怎么來的呢
int rs = resizeStamp(n);
從方法名上看,這個值叫擴容標識戳。
我們就以n=16為例,
static final int resizeStamp(int n) { return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1)); }
Integer.numberOfLeadingZeros(n)就不分析具體實現了,該方法的含義是一個數的最高位第一個非0開始算起,到他的最高位有多少個0。
比如n=16,那么 Integer.numberOfLeadingZeros(n)返回值就是27.
因為16是 10000。一個int是32位,32-5就是27.
(1 << (RESIZE_STAMP_BITS - 1)),其中 RESIZE_STAMP_BITS = 16。 也就是說該表達式的值是一個1,后面跟15個0.這里請注意,這個1的事情。
然后呢,兩個值做或運算,那么剛才的1就會讓這個返回的int值的低16位中的第 16位是1.
我們再看
U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)
(rs << RESIZE_STAMP_SHIFT) + 2)
rs左移16位,還記得剛才說的1嘛,最高位是符號位,所以這個事肯定是一個負數。同時左移16位后,低16位就全部是0。然后加2.那么不看高16位,低16位的值就是2。
這也就是sizeCtl低16位表示的是參與擴容的線程數 + 1。
當最后一個擴容線程准備收尾的判斷依據也是 sizeCtl-1 后是不是等於1。
以上就是sizeCtl的意義,總結下就是高16位是擴容標識戳,低16位是擴容線程數+1。