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