千家信息网

ThreadLocal的结构是什么

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,本篇内容主要讲解"ThreadLocal的结构是什么",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"ThreadLocal的结构是什么"吧!内容大纲Java
千家信息网最后更新 2026年11月02日ThreadLocal的结构是什么

本篇内容主要讲解"ThreadLocal的结构是什么",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"ThreadLocal的结构是什么"吧!

内容大纲

Java对象引用级别

在聊 ThreadLocal 前,先做前置知识铺垫,谈谈Java对象引用级别。

为了使程序能更灵活地控制对象生命周期,从 JDK1.2 版本开始,JDK把对象的引用级别由高到低分为强引用、软引用、弱引用、虚引用四种级别。

强引用 StrongReference

强引用是我们最常见的对象,它属于不可回收资源,垃圾回收器(后面简称G C)绝对不会回收它,即使是内存不足,J V M宁愿抛出 OutOfMemoryErrorM 异常,使程序终止,也不会来回收强引用对象。

软引用 SoftReference

如果对象是软引用,那它的性质属于可有可无,因为内存空间充足的情况下,G C不会回收它,但是内存空间紧张,G C发现它仅有软引用,就会回收该对象,所以软引用对象适合作为内存敏感的缓存对象。

只有对象仅被 WeakReference 引用,它才是弱引用级别对象,因为对象可以在多处被引用,所以 WeakReference 引用的对象,它可能在其他处被强引用了。

虚引用 PhantomReference

顾名思义,虚引用形同虚设,与其他几种引用不同,虚引用不会决定对象的生命周期。

如果一个对象仅有虚引用,那它就和没有任何引用一样,任何时候都可能被 G C 回收。

ThreadLocal

ThreadLocal很多地方叫线程本地变量,也有些地方叫线程本地存储,其实意思差不多。ThreadLocal为变量在每个线程中都创建了一个副本,每个线程可以访问自己内部的副本变量。

ThreadLocal是什么

Thread类声明了成员变量threadLocals,threadLocals才是真正的线程本地变量,因此每个 Thread 都有自己的线程本地变量,所以线程本地变量拥有线程隔离特性,也就是天生的线程安全。

从上图可以看到 threadLocals 成员变量类是 ThreadLocal.ThreadLocalMap,即是 ThreadLocal 提供的内部类,因此 Thread 线程本地变量的创建、新增、获取、删除实现核心,必然是围绕 threadLocals,所以开发者也是围绕 threadLocals 实现功能,为了后续重复使用,还会对代码实现进行封装复用,而 ThreadLocal 就是线程本地变量工具类,由 J D K 提供,线程本地变量的功能都已经实现好了,开箱即用,造福广大开发人员。

ThreadLocal常用的方法

  • set:为当前线程设置变量,当前ThreadLocal作为索引

  • get:获取当前线程变量,当前ThreadLocal作为索引

  • initialValue(钩子方法需要子类实现):赖加载形式初始化线程本地变量,执行get时,发现线程本地变量为null,就会执行initialValue的内容

  • remove:清空当前线程的ThreadLocal索引与映射的元素

现在总结出「本地线程变量的作用域,属于当前线程整个范围,一个线程可以跨越多个方法使用本地线程变量」,当你希望某些变量在某 Thread 的多个方法中共享 并保证线程安全,那就大胆的使用ThreadLocal(ps:一定要想清楚,是某个变量被Thread生命周期内多个方法共享,还是多个Thread共享这个变量!)。

ThreadLocal源码

先来看看User类实现的线程本地变量代码

通过上图,相信大伙对 ThreadLocalMap 结构已经非常清晰,不知有没有细心的小伙伴发现 ThreadLocal 竟被弱引用持有?

为什么ThreadLocal会被弱引用?这块疑惑后面会给大伙安排的明明白白,最后上一张 ThreadLocalMap 源码图。

步骤如下

  1. 获取当前线程

  2. 获取当前线程的本地变量

  3. 线程本地变量没有被创建,执行setInitialValue方法进行初始化,并返回value值

  4. 线程本地变量存在,ThreadLocal计算成索引从 本地线程变量 获取Entry,如果Entry为null,执行setInitialValue方法进行初始化,并返回value值,否则通过Entry获取value返回

initialValue方法

步骤如下

  1. 获取当前线程

  2. 获取线程本地变量

  3. 本地变量不为空,当前ThreadLocal为索引设置映射的value,否则创建线程本地变量再做后续的设置操作

remove 清除变量

为何采用弱引用

为什么 Entry 中对 ThreadLocal 使用弱引用?反问一句,如果使用强引用,会发生什么事情?

我们不知道 key 是什么,如何去获取映射的value,同样的道理,都没有入口去获取到ThreadContextTest.ThreadLoca,自然没办法获取映射的Entry元素。

设计中采用Map结构存储数据,却不能通过key去获取value,这设计明显不合理,又因key、value值是强引用,导致 G C 无法回收,造成内存溢出。

所以针对这种不合理的设计场景 J D K 做了优化,对 Entry 中的 ThreadLocal 使用弱引用,当 G C 发现它仅有弱引用的时候,会进行回收。

remove背后的意义

还没结束,上面留了个小尾巴,大伙都知道 Entry 中对 ThreadLocal 使用弱引用,但value是强引用,如果出现上面提到的不合理场景,value值无法清理,最终内存溢出。

其实value作为强引用设计属于合理,如果用软或弱引用,就出大问题了,程序跑着跑着突然get到了一个null,估计都得骂娘了,所以为解决内存溢出问题 J D K提供remove方法,使开发人员可以选择手动清理整个Entry元素,防止内存溢出。

还记的之前说过吗?线程本地变量的生命周期与线程绑定,一般线程的生命周期比较短,线程结束时,线程本地变量自然就销毁了,软引用与 remove 会不会有点多余了?

业务瞬息万变,大部分情况来说线程的生命周期比较短,但也业务场景会导致线程的生命周期较长,甚至可能线程无限循环执行,这些是你没办法预料到的,数量一旦上来很容易内存溢出,所以个人建议使用完之后及时清理ThreadLocal,理由如下

  • 生命周期较长的线程场景

  • 无限循环线程的场景

  • 线程池场景(因为线程池可以复用线程,而且公司使用的框架可能会定制化线程池,你不能保证他会在线程池内帮你remove)

到此,相信大家对"ThreadLocal的结构是什么"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

线程 变量 对象 方法 内存 周期 生命 场景 结构 索引 级别 内容 多个 设计 不合理 元素 程序 开发 安全 上图 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 精品咖啡豆 互联网科技工作委员会副主任毛军 互联网科技公司税收优惠 关于新基建和互联网科技 北京互联网科技 互联网金融与金融科技 上海互联网科技有限公司 互联网科技大会案例 语蝶互联网科技有限公司 花神(南京)互联网科技有限公司 速维有品互联网信息科技(苏州)有限公司 互联网科技大奖都有哪些公司 国大互联网教育科技有限公司工作 合肥互联网科技公司公章图片 怎样加入科技互联网 雨阳互联网卡科技有限公司 21世纪互联网科技成就 互联网公司属于科技吗 绵阳真晨互联网科技有限公司 高科技和互联网的现代国家 中国互联网科技工作委员会毛军 广州互联网科技公司 合肥互联网科技公司 致聘互联网科技 人人互联网科技是培训 左右互联网络科技 青海省互联网新科技 央行互联网金融科技公司 杭州壁岐互联网科技有限公司 互联网对科技影响
0