Java中ThreadLocalMap解决Hash冲突的实现方式

 更新时间:2025年04月24日 09:48:41   作者:灰_灰丶灰  
本文主要介绍了Java中ThreadLocalMap解决Hash冲突的实现方式,主要方式是使用线性探测法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

ThreadLocalMap 解决哈希冲突的主要方式是使用线性探测法(linear probing)。这种方法通过线性探测来寻找空槽位,以应对哈希冲突。以下是详细的解决方案:

1. 线性探测法

线性探测法在发生哈希冲突时,通过检查数组中的下一个位置来找到空的槽位。如果当前槽位已被占用,它将继续检查下一个槽位,直到找到空槽位或合适的槽位为止。

2. ThreadLocalMap 实现中的哈希冲突解决

ThreadLocalMap 通过以下方法来处理哈希冲突:

  • 计算索引

    • 使用 ThreadLocal 的哈希码来计算在 Entry 数组中的索引。
    • 该索引由 ThreadLocal 实例的 threadLocalHashCode 和数组长度(减去 1)按位与运算得到。
  • 线性探测

    • 如果计算出的索引位置已经被占用(即已有 Entry 对象),ThreadLocalMap 会通过线性探测法检查数组中的下一个位置,直到找到一个空槽位或适合的槽位。
    • 使用 nextIndex 方法来实现线性探测,它将当前位置增加 1,循环回到数组的开头。
  • 处理过时条目

    • 如果在探测过程中遇到 Entry 的键为 null(即过时的条目),ThreadLocalMap 会调用 expungeStaleEntry 方法来清理这些过时条目,并将当前条目插入到合适的位置。

关键代码解析

以下是 ThreadLocalMap 中处理哈希冲突的相关代码:

static class ThreadLocalMap {

    // 存储条目的数组
    private Entry[] table;
    
    // 存储条目的数量
    private int size = 0;
    
    // 定义初始容量
    private static final int INITIAL_CAPACITY = 16;

    // 构造函数
    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;
    }

    // 设置值
    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;
    }
}

重要细节

  • 计算索引

    • i = key.threadLocalHashCode & (len - 1); 使用 ThreadLocal 的哈希码和表长度减去 1 的按位与运算来计算索引。
  • 线性探测

    • nextIndex(i, len) 方法计算下一个索引位置,用于探测冲突。
    • 如果发生冲突,会在 table 数组中继续查找,直到找到空槽位。
  • 清理和重哈希

    • 使用 expungeStaleEntry 方法清理过时的条目。
    • resize() 方法扩展数组并重新分配条目,以减少冲突并提高性能。

总结

ThreadLocalMap 使用线性探测法解决hash冲突

到此这篇关于Java中ThreadLocalMap解决Hash冲突的实现方式的文章就介绍到这了,更多相关threadlocalmap解决hash冲突内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java 全角半角字符转换如何实现

    java 全角半角字符转换如何实现

    在java中可能会用到过全角半角字符转换问题,于是网上搜索整理了一下,晒出来和大家分享,希望可以帮助你们
    2012-12-12
  • 详细解释什么是 Spring Bean(示例详解)

    详细解释什么是 Spring Bean(示例详解)

    Spring Bean 是由 Spring IoC 容器管理的对象实例,也是 Spring 框架的基本组件之一,本文通过示例代码介绍Spring Bean 的作用域(Bean Scope)的相关使用方法,感兴趣的朋友一起看看吧
    2023-09-09
  • Java 通过API操作GraphQL

    Java 通过API操作GraphQL

    这篇文章主要介绍了Java 通过API操作GraphQL的方法,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-05-05
  • Go Java算法重复的DNA序列详解

    Go Java算法重复的DNA序列详解

    这篇文章主要为大家介绍了Go Java算法之重复的DNA序列的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Mybatis或Mybatis-Plus框架的xml文件中特殊符号的使用详解

    Mybatis或Mybatis-Plus框架的xml文件中特殊符号的使用详解

    这篇文章主要介绍了Mybatis或Mybatis-Plus框架的xml文件中特殊符号的使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • JVM中-D、-X、-XX参数的区别

    JVM中-D、-X、-XX参数的区别

    本文主要介绍了JVM中-D、-X、-XX参数的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • 关于SpringSecurity Context 中获取和更改当前用户信息的问题

    关于SpringSecurity Context 中获取和更改当前用户信息的问题

    SpringSecurityContext在异步线程中无法获取用户信息,因其与请求线程绑定;此外,用户信息更新后跳转页面时,身份会被降级为匿名,导致信息无法及时同步,本文给大家介绍SpringSecurity Context 中获取和更改当前用户信息的问题,感兴趣的朋友一起看看吧
    2024-09-09
  • selenium-java实现自动登录跳转页面方式

    selenium-java实现自动登录跳转页面方式

    利用Selenium和Java语言可以编写一个脚本自动刷新网页,首先,需要确保Google浏览器和Chrome-Driver驱动的版本一致,通过指定网站下载对应版本的浏览器和驱动,在Maven项目中添加依赖,编写脚本实现网页的自动刷新,此方法适用于需要频繁刷新网页的场景,简化了操作,提高了效率
    2024-11-11
  • springboot-2.3.x最新版源码阅读环境搭建(基于gradle构建)

    springboot-2.3.x最新版源码阅读环境搭建(基于gradle构建)

    这篇文章主要介绍了springboot-2.3.x最新版源码阅读环境搭建(基于gradle构建),需要的朋友可以参考下
    2020-08-08
  • 如何将Java打开CSV文件到JTable展示

    如何将Java打开CSV文件到JTable展示

    本文主要介绍了如何将Java打开CSV文件到JTable展示,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03

最新评论