HashCode 计算ThreaLocalMap中没有采用传统的调用ThreadLocal的hashcode方法(继承自object的hashcode) , 而是调用nexthashcode , 源码如下:
private final int threadLocalHashCode = nextHashCode();private static AtomicInteger nextHashCode = new AtomicInteger(); //1640531527 能够让hash槽位分布相当均匀private static final int HASH_INCREMENT = 0x61c88647; private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT);}
Hash冲突和HashMap的最大的不同在于 , ThreadLocalMap解决Hash冲突的方式就是简单的步长加1或减1及线性探测 , 寻找下一个相邻的位置 。
/** * Increment i modulo len. */private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0);}/** * Decrement i modulo len. */private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1);}
ThreadLocalMap采用线性探测的方式解决Hash冲突的效率很低 , 如有大量不同的ThreadLocal对象放入map中时发生冲突 。所以建议每个线程只存一个变量(一个ThreadLocal)就不存在Hash冲突的问题 , 如果一个线程要保存set多个变量 , 就需要创建多个ThreadLocal , 多个ThreadLocal放入Map中时会极大的增加Hash冲突的可能 。
清楚意思吗?当你在一个线程需要保存多个变量时 , 你以为是多次set?你错了你得创建多个ThreadLocal , 多次set的达不到存储多个变量的目的 。
sThreadLocal.set("这是在线程a中");
Key的弱引用问题看看官话 , 为什么要用弱引用 。
To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys.
为了处理非常大和生命周期非常长的线程 , 哈希表使用弱引用作为 key 。
- 生命周期长:暂时可以想到线程池中的线程
- key 如果使用强引用:引用的ThreadLocal的对象被回收了 , 但是ThreadLocalMap还持有ThreadLocal的强引用 , 如果没有手动删除 , ThreadLocal不会被回收 , 导致Entry内存泄漏 。
- key 使用弱引用:引用的ThreadLocal的对象被回收了 , 由于ThreadLocalMap持有ThreadLocal的弱引用 , 即使没有手动删除 , ThreadLocal也会被回收 。value在下一次ThreadLocalMap调用set,get , remove的时候会被清除 。
Java8中for循环遍历整个Entry数组 , 遇到key=null的就会替换从而避免内存泄露的问题 。
private int expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; // expunge entry at staleSlot tab[staleSlot].value = null; tab[staleSlot] = null; size--; // Rehash until we encounter null Entry e; int i; for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal<?> k = e.get(); if (k == null) { e.value = null; tab[i] = null; size--; } else { int h = k.threadLocalHashCode & (len - 1); if (h != i) { tab[i] = null; while (tab[h] != null) h = nextIndex(h, len); tab[h] = e; } } } return i; }
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 阿里的垃圾怎么回收?Java G1源码分析与调优手册
- Spring 源码第一篇开整!配置文件是怎么加载的?
- Redis如何清除过期key? 一篇文章带你走近源码!
- 花茶未来市场探析,花茶的感官鉴赏
- Window环境下编译Flink1.10源码
- Java自动化办公OA管理系统源码
- 从源码层,拆解OracleJDK和OpenJDK有什么区别?
- Linux 安装mysql5.7.29源码安装
- redis各类型数据结构和底层实现源码分析
- 中国茶叶之品牌及渠道构建密码探析