ConcurrentHashMap的擴容與資料遷移

2021-10-22 16:39:11 字數 3126 閱讀 4875

目錄

擴容的條件

transfer擴容

資料遷移

高低位的作用

while (s >= (long)(sc = sizectl) && (tab = table) != null &&

(n = tab.length) < maximum_capacity)

else if (u.compareandswapint(this, sizectl, sc,

(rs << resize_stamp_shift) + 2))//5

transfer(tab, null);

s = sumcount();

}

生成此次擴容的唯一擴容戳,基於當前node陣列生成。

sc<0代表有執行緒正在擴容。

有執行緒正在擴容的情況下加入到擴容大軍中,將sizectl加一

如果 沒有執行緒正在擴容,則將sizectl設定為乙個負數(rs << resize_stamp_shift) + 2

private final void transfer(node tab, node nexttab)  catch (throwable ex) 

nexttable = nexttab;

transferindex = n;

}int nextn = nexttab.length;

forwardingnodefwd = new forwardingnode(nexttab);//2

boolean advance = true;

boolean finishing = false; // to ensure sweep before committing nexttab

for (int i = 0, bound = 0;;)

else if (u.compareandswapint

(this, transferindex, nextindex,

nextbound = (nextindex > stride ?

nextindex - stride : 0)))

}if (i < 0 || i >= n || i + n >= nextn)

if (u.compareandswapint(this, sizectl, sc = sizectl, sc - 1))

}else if ((f = tabat(tab, i)) == null)

advance = castabat(tab, i, null, fwd);

else if ((fh = f.hash) == moved)

advance = true; // already processed

else

}if (runbit == 0)

else

for (nodep = f; p != lastrun; p = p.next)

settabat(nexttab, i, ln);

settabat(nexttab, i + n, hn);

settabat(tab, i, fwd);

advance = true;

}else if (f instanceof treebin)

else

}ln = (lc <= untreeify_threshold) ? untreeify(lo) :

(hc != 0) ? new treebin(lo) : t;

hn = (hc <= untreeify_threshold) ? untreeify(hi) :

(lc != 0) ? new treebin(hi) : t;

settabat(nexttab, i, ln);

settabat(nexttab, i + n, hn);

settabat(tab, i, fwd);

advance = true;}}

}}

fh是當前node的hash值,如果》0則表示這個節點還沒有被其他執行緒處理過。通過fh&n(與運算,n是舊node陣列的長度)得到runbit。遍歷鍊錶對runbit進行修改,最後runbit為0則將其設定為低位節點,否則設定為高位節點。

2.構造高位以及低位的鍊錶,即將鍊錶分為高位和地位兩個鍊錶,將低位鍊錶的位置保持不動(即舊的node陣列中是什麼下標,那麼在新的node陣列中就是什麼下標),將高位鍊錶的位置由原先的下標n放到新node陣列的n+16(擴容增加的長度)。

首先在putval()方法中有這樣一句**tabat(tab, i = (n - 1) & hash)來獲取node陣列某一下標的節點,所以很明顯 (n - 1) & hash就是用來獲取下標的,這個代表當前node陣列的最大下標數和key的hash值的與運算。

設某個hash值為20,二進位制0001 0100

當長度為16時,15&20=0000 1111 & 0001 0100=0000 0100=4

當長度為32時(擴容後),31&20=0001 1111 & 0001 0100 =0001 0100=20

可以看到,相同hash在不同的node陣列長度的情況下得到的下標是不一樣的。

再來看,當hash值為60時

60 & 15=0011 1100&0000 1111=0000 1100=12

60 & 31=0011 1100&0001 1111=0001 1100=28

可以看到兩種hash在不同的node陣列長度下,當計算下標不同時,相差都是16,也就是擴容的長度。

說白了,就是在遷移某個下標的鍊錶時,當發現鍊錶節點的hash和舊陣列長度進行與運算fh & n的第x位為1時(為1時是高位,為0時是低位),說明這個節點的hash在擴容後和(新陣列長度-1)的與運算得到的下標與原下標不同,而這個差值剛好是擴容長度16(32-16)。所以將高位直接遷移到原陣列下標+擴容長度(16)的位置。

RabbitMQ的資料日誌存放以及資料遷移

一 rabbitmq的資料和日誌的儲存位置 想找到這個儲存位置,一般我們就可以先去該檔案路徑檢視有沒有最近的日誌和資料,那為什麼要看有沒有最近日期的呢?答 因為日誌和資料的位置是可以人為改變的,如果日誌和資料的最新日期已經停留在很久之前並且佇列一直都是在使用的,那麼99 的可能都是因為儲存位置改變了...

Oracle資料庫的備份 遷庫

oracle資料庫有三種常用的備份方法,分別是匯出 匯入 exp imp 或者使用資料幫浦方法 impdp expdp 熱備份和冷備份。匯出 匯入備份是一種邏輯備份,相對於匯出 匯入來說,熱備份 冷備份是一種物理備份 一 exp 我們知道採用direct path可以提高匯出速度。所以,在使用exp...

linux資料遷移,資料擴容與縮容

建立pv 必須對整體進行pv化 pvcreate dev 磁碟名 pvs 檢視磁碟是否有pv化 pvscan 得到回顯建立vg vgcreate 卷組名 dev 磁碟名 vgscan 得到回顯 vgs 顯示磁碟容量建立lv lvcreate l 邏輯卷大小 n 邏輯卷名 卷組名 lvscan 得到回...