再看一下具体的遍历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;}}}5. ThreadLocal使用注意事项使用ThreadLocal结束 , 一定要调用remove方法 , 清理掉threadLocal数据 。具体流程类似下面这样:/** * @author 一灯架构 * @apiNote ThreadLocal示例 **/public class ThreadLocalDemo {// 1. 创建ThreadLocalstatic ThreadLocal<User> threadLocal = new ThreadLocal<>();public void method() {try {User user = getUser();// 2. 给threadLocal赋值threadLocal.set(user);// 3. 执行其他业务逻辑doSomething();} finally {// 4. 清理threadLocal数据threadLocal.remove();}}}如果忘了调用remove方法 , 可能会导致两个严重的问题:- 导致内存溢出
如果线程的生命周期很长 , 一直往ThreadLocal中放数据 , 却没有删除 , 最终产生OOM
- 导致数据错乱
如果使用了线程池 , 一个线程执行完任务后并不会被销毁 , 会继续执行下一个任务 , 导致下个任务访问到了上个任务的数据 。
6.1 ThreadLocal是怎么保证数据安全性的?ThreadLocal底层使用的ThreadLocalMap存储数据 , 而ThreadLocalMap是线程Thread的私有变量 , 不同线程之间数据隔离 , 所以即使ThreadLocal的set、get、remove方法没有加锁 , 也能保证线程安全 。
经验总结扩展阅读
- 全方位剖析宝宝湿疹
- 深入剖析Sgementation fault原理
- Java程序员必会Synchronized底层原理剖析
- 硬核字幕组是什么意思?
- 硬核是什么梗?
- 硬核颜值是什么意思?
- 硬核父母是什么意思?
- 硬核奶奶是什么意思?
- 硬核祖母是什么意思?
- 硬核女装是什么意思?
