ConcurrentHashMap中sizeCtl的說明


  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。

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM