Java中ThreadLocalMap解決Hash沖突的實(shí)現(xiàn)方式
ThreadLocalMap
解決哈希沖突的主要方式是使用線性探測(cè)法(linear probing)。這種方法通過線性探測(cè)來尋找空槽位,以應(yīng)對(duì)哈希沖突。以下是詳細(xì)的解決方案:
1. 線性探測(cè)法
線性探測(cè)法在發(fā)生哈希沖突時(shí),通過檢查數(shù)組中的下一個(gè)位置來找到空的槽位。如果當(dāng)前槽位已被占用,它將繼續(xù)檢查下一個(gè)槽位,直到找到空槽位或合適的槽位為止。
2. ThreadLocalMap 實(shí)現(xiàn)中的哈希沖突解決
ThreadLocalMap
通過以下方法來處理哈希沖突:
計(jì)算索引:
- 使用
ThreadLocal
的哈希碼來計(jì)算在Entry
數(shù)組中的索引。 - 該索引由
ThreadLocal
實(shí)例的threadLocalHashCode
和數(shù)組長(zhǎng)度(減去 1)按位與運(yùn)算得到。
- 使用
線性探測(cè):
- 如果計(jì)算出的索引位置已經(jīng)被占用(即已有
Entry
對(duì)象),ThreadLocalMap
會(huì)通過線性探測(cè)法檢查數(shù)組中的下一個(gè)位置,直到找到一個(gè)空槽位或適合的槽位。 - 使用
nextIndex
方法來實(shí)現(xiàn)線性探測(cè),它將當(dāng)前位置增加 1,循環(huán)回到數(shù)組的開頭。
- 如果計(jì)算出的索引位置已經(jīng)被占用(即已有
處理過時(shí)條目:
- 如果在探測(cè)過程中遇到
Entry
的鍵為null
(即過時(shí)的條目),ThreadLocalMap
會(huì)調(diào)用expungeStaleEntry
方法來清理這些過時(shí)條目,并將當(dāng)前條目插入到合適的位置。
- 如果在探測(cè)過程中遇到
關(guān)鍵代碼解析
以下是 ThreadLocalMap
中處理哈希沖突的相關(guān)代碼:
static class ThreadLocalMap { // 存儲(chǔ)條目的數(shù)組 private Entry[] table; // 存儲(chǔ)條目的數(shù)量 private int size = 0; // 定義初始容量 private static final int INITIAL_CAPACITY = 16; // 構(gòu)造函數(shù) ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; } // 線性探測(cè)法獲取 Entry private Entry getEntry(ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); } // 線性探測(cè)法后處理 private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null) expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null; } // 設(shè)置值 private void set(ThreadLocal<?> key, Object value) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len - 1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } // 獲取下一個(gè)索引 private int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); } // 替換過時(shí)條目 private void replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot) { Entry[] tab = table; int len = tab.length; Entry e; int slotToExpunge = staleSlot; for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) { if (e.get() == null) slotToExpunge = i; } for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal<?> k = e.get(); if (k == key) { e.value = value; tab[i] = tab[staleSlot]; tab[staleSlot] = e; if (slotToExpunge == staleSlot) slotToExpunge = i; cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); return; } if (k == null && slotToExpunge == staleSlot) slotToExpunge = i; } tab[staleSlot].value = null; tab[staleSlot] = new Entry(key, value); if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); } // 處理過時(shí)條目 private int expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; tab[staleSlot].value = null; tab[staleSlot] = null; size--; Entry e; int i; for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { if (e.get() == null) { e.value = null; tab[i] = null; size--; } else { int h = e.get().threadLocalHashCode & (len - 1); if (h != i) { tab[i] = null; while (tab[h] != null) h = nextIndex(h, len); tab[h] = e; } } } return i; } // 清理某些槽位 private boolean cleanSomeSlots(int i, int n) { boolean removed = false; Entry[] tab = table; int len = tab.length; do { i = nextIndex(i, len); Entry e = tab[i]; if (e != null && e.get() == null) { n = len; removed = true; i = expungeStaleEntry(i); } } while ((n >>>= 1) != 0); return removed; } // 重新哈希 private void rehash() { expungeStaleEntries(); if (size >= threshold - threshold / 4) resize(); } private void expungeStaleEntries() { Entry[] tab = table; int len = tab.length; for (int j = 0; j < len; j++) { Entry e = tab[j]; if (e != null && e.get() == null) expungeStaleEntry(j); } } private void resize() { Entry[] oldTab = table; int oldLen = oldTab.length; int newLen = oldLen * 2; Entry[] newTab = new Entry[newLen]; int count = 0; for (int j = 0; j < oldLen; ++j) { Entry e = oldTab[j]; if (e != null) { ThreadLocal<?> k = e.get(); if (k == null) { e.value = null; } else { int h = k.threadLocalHashCode & (newLen - 1); while (newTab[h] != null) h = nextIndex(h, newLen); newTab[h] = e; count++; } } } setThreshold(newLen); size = count; table = newTab; } }
重要細(xì)節(jié)
計(jì)算索引:
i = key.threadLocalHashCode & (len - 1);
使用ThreadLocal
的哈希碼和表長(zhǎng)度減去 1 的按位與運(yùn)算來計(jì)算索引。
線性探測(cè):
nextIndex(i, len)
方法計(jì)算下一個(gè)索引位置,用于探測(cè)沖突。- 如果發(fā)生沖突,會(huì)在
table
數(shù)組中繼續(xù)查找,直到找到空槽位。
清理和重哈希:
- 使用
expungeStaleEntry
方法清理過時(shí)的條目。 resize()
方法擴(kuò)展數(shù)組并重新分配條目,以減少?zèng)_突并提高性能。
- 使用
總結(jié)
ThreadLocalMap
使用線性探測(cè)法解決hash沖突
到此這篇關(guān)于Java中ThreadLocalMap解決Hash沖突的實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)threadlocalmap解決hash沖突內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 全角半角字符轉(zhuǎn)換如何實(shí)現(xiàn)
在java中可能會(huì)用到過全角半角字符轉(zhuǎn)換問題,于是網(wǎng)上搜索整理了一下,曬出來和大家分享,希望可以幫助你們2012-12-12Mybatis或Mybatis-Plus框架的xml文件中特殊符號(hào)的使用詳解
這篇文章主要介紹了Mybatis或Mybatis-Plus框架的xml文件中特殊符號(hào)的使用詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11關(guān)于SpringSecurity?Context?中獲取和更改當(dāng)前用戶信息的問題
SpringSecurityContext在異步線程中無法獲取用戶信息,因其與請(qǐng)求線程綁定;此外,用戶信息更新后跳轉(zhuǎn)頁面時(shí),身份會(huì)被降級(jí)為匿名,導(dǎo)致信息無法及時(shí)同步,本文給大家介紹SpringSecurity?Context?中獲取和更改當(dāng)前用戶信息的問題,感興趣的朋友一起看看吧2024-09-09selenium-java實(shí)現(xiàn)自動(dòng)登錄跳轉(zhuǎn)頁面方式
利用Selenium和Java語言可以編寫一個(gè)腳本自動(dòng)刷新網(wǎng)頁,首先,需要確保Google瀏覽器和Chrome-Driver驅(qū)動(dòng)的版本一致,通過指定網(wǎng)站下載對(duì)應(yīng)版本的瀏覽器和驅(qū)動(dòng),在Maven項(xiàng)目中添加依賴,編寫腳本實(shí)現(xiàn)網(wǎng)頁的自動(dòng)刷新,此方法適用于需要頻繁刷新網(wǎng)頁的場(chǎng)景,簡(jiǎn)化了操作,提高了效率2024-11-11springboot-2.3.x最新版源碼閱讀環(huán)境搭建(基于gradle構(gòu)建)
這篇文章主要介紹了springboot-2.3.x最新版源碼閱讀環(huán)境搭建(基于gradle構(gòu)建),需要的朋友可以參考下2020-08-08