应届生投腾讯,被面试官问了8个和 ThreadLocal 相关的问题。
问:谈一谈ThreadLocal的结构。
ThreadLocal内部维护了一个ThreadLocalMap静态内部类,ThreadLocalMap中又维护了一个Entry静态内部类,和Entry数组。
Entry类继承弱引用类WeakReference,Entry类有一个有参构造函数,参数为ThreadLocal和value值,构造方法函数内部会调用父类有参构造函数,ThreadLocal作为父类有参构造函数的参数。
其底层数据结构可以看成是一个hash表,索引是通过原子类AtomicInteger、HASH_INCREMENT( = 0x61c88647)和Entry[ ]的长度而来。
索引 = ThreadLocal#nextHashCode & (Entry[]#length - 1)nextHashCode = AtomicInteger#getAndAdd(HASH_INCREMENT)
问:你知道ThreadLocal是如何保证线程隔离的么?
在Thread内部,维护了ThreadLocal.ThreadLocalMap这个对象threadLocals,在Thread.currentThread获取当前线程时,会初始化当前线程的threadLocals。
也就是说,每个Thread内部都有一个ThreadLocalMap,ThreadLocalMap伴随着Thread的整个生命周期,也会随着线程的销毁而终结

问:你知道ThreadLocal和Synchronized的区别吗?
都能对数据进行线程隔离吧,Synchronized是用时间换空间,ThreadLocal使用空间换时间。
问:为什么ThreadLocal所谓的Key(ThreadLocal)为弱引用,为什么Value不能为弱引用对象呢?
对于key(ThreadLocal)为弱引用问题: 如果key为强引用,引用的ThreadLocal的对象被回收了,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。
反之,如果key为弱引用, 引用的ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set,get,remove的时候会被清除。
对于value为什么为强引用不使用弱引用的问题:如果value为弱引用,当value对象被回收了,ThreadLocalMap还持有value的弱引用,也会被回收,这样就会出现,存在key值,而value值不存在,这样的情况是不允许的。所以使用value使用强引用,当key被回收掉,在调用set、get、remove方法时会将key失效的value值清除掉。
问:ThreadLocal会造成内存泄漏么?
使用完ThreadLocal,没有正确的调用remove方法去清理,就会造成内存泄漏,虽然调用set和get方法的时候也会清理失效的key和对应的value,但是这并不是及时的,比如下面这个案例:
每个线程恰好只使用了一次set方法,没有及时地调用remove方法,这样很容易造成内存泄露,知道内存溢出。
import java.util.concurrent.*;
/*** -Xmx5m :设置堆内存为5m*/
public class ThreadLocalTest {static ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();static ThreadPoolExecutor executorService = new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS,new LinkedBlockingDeque<>(100),Executors.defaultThreadFactory());public static void main(String[] args) {executorService.execute(() -> {threadLocal.set(new byte[1024 * 1024 * 2]);});executorService.execute(() -> {threadLocal.set(new byte[1024 * 1024 * 2]);});}
}
问:那如何才能正确的使用ThreadLocal呢?
- 用 private fianl static 修饰,主要使用为threadLocal作为每个线程内部的map的key,所以不需要总是创建,维持一个就可以,再者是因为static修饰,其就属于类本身,生命周期跟随类一致。因为ThreadLocal作为key,并且为弱引用,所以用private fianl static修饰,会防止threadLocal被gc掉,防止内存泄漏。
- 当然,在finally块中调用ThreadLocal#remove方法,就显得尤为重要。否则不仅可能会造成内存泄漏,在使用线程池的情况下还可能会读到脏数据。
问:那你谈谈ThreadLocal底层的这个hash表
这个hash表是一个Entry[],默认容量的是16,扩容因子是2/3,通过开放寻址法解决hash冲突,每次的索引值是通过如下得到(伪代码)
索引 = ThreadLocal#nextHashCode & (Entry[]#length - 1)// 保证索引的原子性
nextHashCode = AtomicInteger#getAndAdd(HASH_INCREMENT)
HASH_INCREMENT魔术值为0x61c88647,这个数是通过斐波那契散列求出来的
魔数 = 黄金分割比 (0.618)*2^32
每次扩容都会扩大2倍,这样的好处是减少Hash碰撞,让数据更散列更均匀的分布,更充分的利用数组的空间。
原因如下:
当数组的长度为2的幂次方时,len - 1的二进制为1...1...1,做&运算时,取决于key.threadLocalHashCode,也就是说key.threadLocalHashCode本身符合均匀分布,Hash算法的结果就是均匀的。
索引 = key.threadLocalHashCode & (len - 1)
在set方法的时候,如果发现有失效的key,就会去清除失效的key和对应的value。
清理的逻辑是:
从数组的当前坐标(失效key)向前遍历,找到最前面的失效的key,记录下来(假设此位置为a)。
再从数组的当前坐标(失效key)向后遍历,此时
- 如果遇到同样的key就先替换,再开始从记录下来的位置a到此位置开始清理,清理过程中,如果发现此区间内存在有效的key,那么将这些key重新hash,放到别的位置上(因为采用了开放寻址法)
- 如果没遇到同样的key,找到最后的失效的key,在这个区间开始清理。




问:你知道父线程和子线程如何共享ThreadLocal吗?
可以用InheritableThreadLocal
InheritableThreadLocal实现子线程可以访问父线程的线程变量的实现原理如下:
- InheritableThreadLocal通过重写createMap 和 getMap 方法让本地变量保存到了具体线程的inheritableThreadLocal变量中
- 线程通过调用inheritableThreadLocal实例的set或get方法时,就会创建当前线程的inheritableThreadLocal变量
- 当父线程创建子线程时,构造函数会把父线程中的inheritableThreadLocal变量里面的本地变量值复制一份保存到子线程的inheritableThreadLocal变量里
案例:
public class ThreadLocalTest {static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();static ThreadPoolExecutor executorService = new ThreadPoolExecutor(10,20,60,TimeUnit.SECONDS,new LinkedBlockingDeque<>(100),Executors.defaultThreadFactory());public static void main(String[] args) {threadLocal.set("hello");executorService.execute(() -> {System.out.println(threadLocal.get() + "=====1");});executorService.execute(() -> {System.out.println(threadLocal.get() + "=====2");});}
}
参考资料:
spring.io
京东云官方blog
货拉拉官方blog
ThreadLocal源码解析
相关文章:
应届生投腾讯,被面试官问了8个和 ThreadLocal 相关的问题。
问:谈一谈ThreadLocal的结构。 ThreadLocal内部维护了一个ThreadLocalMap静态内部类,ThreadLocalMap中又维护了一个Entry静态内部类,和Entry数组。 Entry类继承弱引用类WeakReference,Entry类有一个有参构造函数,参数…...
Linux命令scp用法
本文主要讲的是scp用法如果哪里不对欢迎指出,主页https://blog.csdn.net/qq_57785602?typeblogscp 可以在win系统使用,本文百分之八十写的是win系统怎么使用,在本地上到服务器文件,从服务器下载文件到本地用工具连接到公司服务器时ÿ…...
数据质量怎么监控
目录 一、任务基线级别 二、任务级别 & 表级别 三、字段级别 1. 对指标字段的监控 2. 对维度字段的监控 四、报表级别监控 五、总结 跑了几场面试,数据质量怎么监控是经常被问到的问题,仅次于自我介绍。 因为数据行业发展了几年,数…...
.NET Core 实现Excel的导入导出
.NET Core 使用NPOI实现Excel的导入导出前言NPOI简介一、安装相对应的程序包1.1、在 “管理NuGet程序包” 中的浏览搜索:“NPOI”二、新建Excel帮助类三、调用3.1、增加一个“keywords”模型类,用作导出3.2、添加一个控制器3.3、编写导入导出的控制器代码…...
排好队,一个一个来:宫本武藏教你学队列(附各种队列源码)
文章目录前言:理解“队列”的正确姿势一个关于队列的小思考——请求处理队列的两大“护法”————顺序队列和链式队列数组实现的队列链表实现的队列循环队列关于开篇,你明白了吗?最后说一句前言: 哈喽!欢迎来到黑洞晓…...
C语言--动态内存管理1
目录前言动态内存函数介绍mallocfreecallocrealloc常见的动态内存错误对NULL指针的解引用操作对动态开辟空间的越界访问对非动态开辟内存使用free释放使用free释放一块动态开辟内存的一部分对同一块动态内存多次释放动态开辟内存忘记释放(内存泄漏)对通讯…...
HTTPS 的工作原理
1、客户端发起 HTTPS 请求 这个没什么好说的,就是用户在浏览器里输入一个 https 网址,然后连接到 server 的 443 端口。 2、服务端的配置 采用 HTTPS 协议的服务器必须要有一套数字证书,可以自己制作,也可以向组织申请…...
游戏开发中建议使用半兰伯特光照
游戏开发中建议使用半兰伯特光照模型 在基本光照模型中求出漫反射部分的计算公式: 漫反射 = 入射光线的颜色和强度(c light) * 材质漫反射系数 (m diffuse)* 表面法线(n) * 其光源防线 (I) 在shader中为了不让 n和i的点乘结果为负数,即使用了saturate函数让值截取在[0,1]区…...
JavaScript到底如何存储数据?
1.var的迷幻操作 普遍的观点:JavaScript中的基本数据类型是保存在栈空间,而引用数据类型则是保存在堆空间里, 是否正确? 浏览器环境下JavaScript变量类型的运行实践结果: var a 10;console.log(a);console.log(window.a); console.log(wind…...
python实战应用讲解-【numpy专题篇】numpy应用案例(一)(附python示例代码)
目录 用Python分析二手车的销售价格 用Python构建GUI应用的铅笔草图 需要的包 实现步骤 完整代码 用Python分析二手车的销售价格 如今,随着技术的进步,像机器学习等技术正在许多组织中得到大规模的应用。这些模型通常与一组预定义的数据点一起工作…...
网络割接项目
某企业准备采购2台华为设备取代思科旧款设备,针对下列问题作出解答。 (1)做设备替换的时候,如何尽可能保证业务稳定性,请给出解决方案。 a)对现网拓扑进行分析,分析现网拓扑的规划(链路类型、cost、互联IP、互联接口等信息)、分析现网流量模型(路由协议、数据流向特…...
SpringBoot整合数据可视化大屏使用
1 前言 DataV数据可视化是使用可视化应用的方式来分析并展示庞杂数据的产品。DataV旨让更多的人看到数据可视化的魅力,帮助非专业的工程师通过图形化的界面轻松搭建专业水准的可视化应用,满足您会议展览、业务监控、风险预警、地理信息分析等多种业务的展示需求, 访问地址:h…...
蓝桥杯Web前端练习题-----水果拼盘
一、水果拼盘 介绍 目前 CSS3 中新增的 Flex 弹性布局已经成为前端页面布局的首选方案,本题可以使用 Flex 属性快速完成布局。 准备 开始答题前,需要先打开本题的项目代码文件夹,目录结构如下: ├── css │ └── style.…...
[攻城狮计划]如何优雅的在RA2E1上运行RT_Thread
文章目录[攻城狮计划]|如何优雅的在RA2E1上运行RT_Thread准备阶段🚗开发板🚗开发环境🚗下载BSP🚗编译烧录连接串口总结[攻城狮计划]|如何优雅的在RA2E1上运行RT_Thread 🚀🚀开启攻城狮的成长之旅࿰…...
1.linux操作命令
1. pwd -> 打印当前绝对工作路径。 2. ls -> 查看目录的文件名 ls -> 默认列出当前目录的全部文件名 ls . -> 列出当前目录的全部文件名(.代表当前目录) ls / -> 列出根目录下的全部文件命名 ls -a -> 列出当前目录下全部文件名(包括隐藏…...
STL--vector
vector 头文件 #include<vector>向量的定义: vector<int> vec;//定义一个vec型的向量a vector<int> vec(5); //定义一个初始大小为5的向量 vector<int> vec(5,1); //初始大小为5,值都为1的向量二维数组࿱…...
Java每日一练(20230324)
目录 1. 链表插入排序 🌟🌟 2. 最接近的三数之和 🌟🌟 3. 寻找旋转排序数组中的最小值 🌟🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一…...
你掌握了吗?在PCB设计中,又快又准地放置元件
在印刷电路板设计中,设置电路板轮廓后,将零件(占地面积)调用到工作区。然后将零件重新放置到正确的位置,并在完成后进行接线。 组件放置是这项工作的第一步,对于之后的平滑布线工作是非常重要的工作。如果在接线工作期间模块不足…...
springboot学生综合测评系统
031-springboot学生综合测评系统演示录像2022开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7(一定要5.7版本) 数据库工具:Navicat11 开发软件&…...
【Unity3D】法线贴图和凹凸映射
1 法线贴图原理 表面着色器中介绍了使用表面着色器进行法线贴图,实现简单快捷。本文将介绍使用顶点和片元着色器实现法线贴图和凹凸映射,实现更灵活。 本文完整代码资源见→法线贴图和凹凸映射。 1)光照原理 Phong 光照模型和 Blinn Phong 光…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
