再看一下实际的set方法源码:
// key就是当前ThreadLocal对象实例,value是ThreadLocal泛型对象值private void set(ThreadLocal<?> key, Object value) {// 获取ThreadLocalMap中的Entry数组Entry[] tab = table;int len = tab.length;// 计算key在数组中的下标 , 也就是ThreadLocal的hashCode和数组大小-1取余int i = key.threadLocalHashCode & (len - 1);// 查找流程:从下标i开始,判断下标位置是否有值,// 如果有值判断是否等于当前ThreadLocal对象实例,等于就覆盖,否则继续向后遍历数组,直到找到空位置for (Entry e = tab[i];e != null;// nextIndex 就是让在不超过数组长度的基础上,把数组的索引位置 + 1e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();// 如果等于当前ThreadLocal对象实例,直接覆盖if (k == key) {e.value = https://www.huyubaike.com/biancheng/value;return;}// 当前key是null,说明ThreadLocal对象实例已经被GC回收了,直接覆盖if (k == null) {replaceStaleEntry(key, value, i);return;}}// 找到空位置,创建Entry对象tab[i] = new Entry(key, value);int sz = ++size;// 当数组大小大于等于扩容阈值(数组大小的三分之二)时,进行扩容if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();}
set方法具体流程如下:

文章插图
从源码和流程图中得知,ThreadLocal是通过线性探测法解决哈希冲突的,线性探测法具体赋值流程如下:
- 通过key的hashcode找到数组下标
- 如果数组下标位置是空或者等于当前ThreadLocal对象,直接覆盖值结束
- 如果不是空 , 就继续向下遍历,遍历到数组结尾后,再从头开始遍历,直到找到数组为空的位置 , 在此位置赋值结束
4.3 get方法源码
// 从ThreadLocal从取值public T get() {// 获取当前线程对象Thread t = Thread.currentThread();// 获取此线程对象中的ThreadLocalMap对象ThreadLocalMap map = getMap(t);if (map != null) {// 通过ThreadLocal实例对象作为key,在Entry数组中查找数据ThreadLocalMap.Entry e = map.getEntry(this);// 如果不为空,表示找到了 , 直接返回if (e != null) {T result = (T)e.value;return result;}}// 如果ThreadLocalMap是null,就执行初始化ThreadLocalMap操作return setInitialValue();}
再看一下具体的遍历Entry数组的逻辑:// 具体的遍历Entry数组的方法private Entry getEntry(ThreadLocal<?> key) {// 通过hashcode计算数组下标位置int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];// 如果下标位置对象不为空,并且等于当前ThreadLocal实例对象 , 直接返回if (e != null && e.get() == key)return e;else// 如果不是 , 需要继续向下遍历Entry数组return getEntryAfterMiss(key, i, e);}
再看一下线性探测法特殊的取值方法:// 如果不是,需要继续向下遍历Entry数组private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {Entry[] tab = table;int len = tab.length;// 循环遍历数组,直到找到ThreadLocal对象,或者遍历到数组为空的位置while (e != null) {ThreadLocal<?> k = e.get();// 如果等于当前ThreadLocal实例对象,表示找到了 , 直接返回if (k == key)return e;// key是null,表示ThreadLocal实例对象已经被GC回收,就帮忙清除valueif (k == null)expungeStaleEntry(i);else// 索引位置+1,表示继续向下遍历i = nextIndex(i, len);e = tab[i];}return null;}// 索引位置+1,表示继续向下遍历,遍历到数组结尾,再从头开始遍历private static int nextIndex(int i, int len) {return ((i + 1 < len) ? i + 1 : 0);}
ThreadLocal的get方法流程如下:
文章插图
4.4 remove方法源码remove方法流程跟set、get方法类似,都是遍历数组 , 找到ThreadLocal实例对象后,删除key、value,再删除Entry对象结束 。
public void remove() {// 获取当前线程的ThreadLocalMap对象ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);}// 具体的删除方法private void remove(ThreadLocal<?> key) {ThreadLocal.ThreadLocalMap.Entry[] tab = table;int len = tab.length;// 计算数组下标int i = key.threadLocalHashCode & (len - 1);// 遍历数组,直到找到空位置,// 或者值等于当前ThreadLocal对象,才结束for (ThreadLocal.ThreadLocalMap.Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {// 找到后 , 删除key、value,再删除Entry对象if (e.get() == key) {e.clear();expungeStaleEntry(i);return;}}}
推荐阅读
- 深入剖析Sgementation fault原理
- Java程序员必会Synchronized底层原理剖析
- 95年属猪运气在2023年是怎么样的 人生三大方面剖析
- 英雄联盟鳄鱼怎么玩才强 深度剖析鳄鱼玩法
- 行车安全剖析 安全行车细节问题盘点
- 剖析七条富贵竹风水摆放禁忌 盘点富贵竹风水摆放禁忌
- 淮南机动车检测机构 淮南车辆上线检验剖析
- 昌吉车辆上线检验剖析 昌吉汽车检测
- 泰拉瑞亚软核中核硬核什么意思 泰拉瑞亚软核中核硬核区别
- 中山车辆检测 中山车辆上线检验剖析是什么