首页
题库
公司真题
专项练习
面试题库
在线编程
面试
面试经验
AI 模拟面试
简历
求职
学习
课程
专栏·文章
竞赛
搜索
我要招人
发布职位
发布职位、邀约牛人
更多企业解决方案
在线笔面试、雇主品牌宣传
登录
/
注册
苏小栋
Java
发布于北京
关注
已关注
取消关注
@牛客276278959号:
threadlocal个人理解
0.threadlocal的应用场景?1.存放数据的容器底层的数据结构是什么样的?2.为什么会内存泄露?3.为什么会设计成两个引用指向同一个threadlocal对象?4.软引用 优化了什么问题?5.创建一个threadlocal对象为何使用static修饰,有哪些本质原因?6.为什么必须通过finally块去调用remove方法清理对象?7.扩容机制带来的内存泄露?0A:A方法调用B方法,B方法调用C方法,如果想要传值,可以通过方法传参或者包装的对象属性进行传参,那如果这个调用链路很长呢?A->B->C->....->Z方法,但是我们发现程序的执行流顺序是自上而下的,那如果我在A点把值存到当前线程上,再在Z方法把值从当前线程上取出来用,那就不需要通过传参的方式去传,因此在 一些框架上通常可以看到XXXContext,其实就是应用了threadlocal。1A:问题0上说明了我们要把值存在线程类里,那在线程类里就会有一个容器,因为线程里面有可能会放很多值嘛,这个容器就是一个ThreadLocalMap,它的底层是一个Entry[],Entry里有两个属性,Key和Value,Value是存的值,Key是当前的threadlocal对象且弱引用指向,key这么去设计问题3会具体说明,虽然ThreadLocalMap也叫map,但是和HashMap在数据结构是有本质区别,ThreadLocalMap仅仅只有一个数组,出现hash冲突不会像HashMap还能添加链表节点或者树节点,ThreadLocalMap没有这些概念,ThreadLocal是使用了线性探测法去找自己的位置,寻址算法是,先去通过key的hash值去计算出数组中的一个位置,如果这个位置没有Entry节点,自己占了,如果有Entry节点,从当前位置往后探测,探测到没有Entry节点占用位置为止,那如果探测到数组的最大下标位置时怎么办,会重新回到下标0位置继续探测,所以 Entry数组,可以理解是一个“数组环”,当然了,在探测过程中有可能遇到一些Entry节点的key部分为null的节点,遇到这种情况呢,会进入这个下标,往前探到null,往后探到null,中间的区域会将key部分为null的节点清理掉,并会将这个区域里的正常节点rehash,那至于为什么会出现key部分为null的节点,其实是和问题4,5有关的。因此,ThreadLocal的哈希冲突有可能确实是计算的位置都一样,也有可能别的Entry节点的位置也被占了,线性探测到当前这个Entry节点上的。2A:由于java线程的特殊性,java线程就是CPU线程的映射,创建线程,销毁线程都存在内核用户态的切换,而且开启一个线程就需要分配栈内存,大量创建有可能造成内存的溢出,因此都是通过线程池去缓存线程,那可以认为这些线程都属于gc root下的对象,难以被回收,因此ThreadLocalMap里的对象都是难以被回收,gc root根可达性算法这个不需要多说吧,因此造成内存泄露。3A:首先上面知道为什么要把值存到线程里了,并且知道存在的容器其实就是一个数组,那么问题来了,Thread类属于一个很重要的类,会给你直接开放取值存值的方法吗,如果这样设计,你还需要不停的去调用Thread.currentThread(),在去通过这个线程去调用取值存值的方法,还需要去在Thread类应对数组的扩容问题,那这个从解耦的角度上讲,是不是就不行,Thread类里面只给你提供一个容器,那至于容器里面存取值或者扩容,我们写一个类给你,并且优雅的给你提供一些解决内存泄露的机制和方法。因此ThreadLocal其实就是一个操作当前线程ThreadLocalMap的门户或者是API。那现在门户有了,该来聊聊存取值的规则了,一个线程容器里面可以存多个值,那该如何区分呢,之前说过嘛,就是Entry节点的key,使用的就是当前ThreadLocal对象,那为什么会采用这样的设计,而不是采用类似于HashMap的设计,那如果采用HashMap的设计,我估计是这样的写法:threadlocal.set(Key, "value"),Key是个Object类型,这个设计思路要求Key这个值在A->B->C->....->Z的调用链路上要做到可见性,最好做成static对象并且还要维护起来,如果真这么玩的话,Threadlocal就肯定做成单例的了。但是你发现本身ThreadLocal这个API入口的对象在调用链路上本身就具有可见性,那干脆直接以自己为key,那就是现在的写法threadlocal.set("value"),因此要想多存几个值,你就需要多个ThreadLocal对象,因此ThreadLocal对象是多例的。当然了,很多人就得反驳了,就单单是这一点好处显然站不住脚,这只是原因1,原因2,得从问题4上去寻找答案。4A:从问题3可以得出,有两个引用指向了同一个ThreadLcoal对象,外面是个强引用,里面key部分是个弱引用。那为什么设计成弱引用,假设现在有个场景,我通过ThreadLocal将值放入线程容器里面,在还没有清理值之前,突然访问线程容器的门户消失了,因为你不可能null.remove()吧,也有人说为什么会出现这种情况,比方说外面强引用挂载的那个对象被gc回收了,是不是就会出现外面的强引用消失,那现在有且仅有一个弱引用指向这个ThreadLocal对象,这种情况下在下一次gc收集时就会把这个ThreadLocal对象清掉,这个时候Entry节点的key部分会“快速”反应为null,这样会有一个好处,等这个线程重新被其他业务请求拿到,框架层面或者业务本身层面使用到ThreadLocal时,做线性探测找自身Entry节点位置时,探测过程中key==null为条件,发现这类Entry节点时,会帮你清理,但是不要过于依赖这套优化机制,比如说人家一下子就找到自己的位置,没有进行探测,也没触发扩容(扩容会做一次全局清理),因此软引用只是针对外面强引用在手动清理值之前突然消失处理的一套优化机制。由此可以得出Entry数组里面的节点key部分为null的就是有问题的,那key部分不为null就没有问题吗,这个问题要带入到 问题6里面去回答。5A:其实这个问题隐隐约约在问题3里面已经回答了,因为本身ThreadLocal在调用链路上是具有可见性的,所以做成静态变量是非常方便调用链路上的方法引入使用的,当然这只是原因其中之一,还有一个原因就是static变量是gc root下的一个分支,这就保证了ThreadLocal对象难以被回收,那问题来了,这种情况下问题4中key为null还会出现吗,答案是不会,ThreadLocal对象一直在堆内存中,所以当你使用了static去做修饰,清理的代码永远不会走到,软引用的优化仅仅是针对外面强引用在手动清理值之前突然消失。6A:问题5说过当我们static修饰后,保证了ThreadLocal不会被回收,还记得问题4最后说的问题吗,key部分不为null就保证一定不是泄露对象吗?很显然不一定,如果我们没有手动清除,更为准确的说是不在finally块里面调用remove方法清理对象,都有可能产生内存泄露问题,你只set值,不remove或者不在finally块调用remove,之前抛出异常了。这两种情况都会产生内存泄露而且没有办法解决,因为ThreadLocal不会被回收,不能依靠软引用机制去清理了,此时内存泄露对象和非内存泄露对象key部分都是非null,泄露对象和正常的对象“如出一辙”,因此在内部机制是没有办法解决的,这也是为什么官方必须提供一个remove方法让大家强制调用,所以在使用ThreadLocal过程中,在一次请求的生命周期内,必须要做到用完就清理的好习惯!7A:从问题1可以了解Entry容器是一个数组,但是为什么设计成数组而不是一个链表,众所周知,数组的唯一优势就是可以从下标索引直接访问,劣势就是会占用连续的内存,如果是设计成链表,定位位置会随着链表的长度导致迭代的复杂度增加,好处就是不需要连续的内存,可以利用链表的特性形成真正的“数组环”。很显然,官方选择了数组,虽然定位很快(存在探测有可能效率降低),但是内存问题也会随之而来,令人忽视。假设现在有n个ThreadLocal,分别set一次和remove一次,操作次数总数就是2n,情景1:前n次都是set,后n次都是remove;情景2:n次set和n次remove交叉进行。请问对于我们使用ThreadLocal更希望趋向哪一种情景,很显然是情景2,因为情景1前面都是在set,有可能会将Entry数组不停的去扩容,导致分配了很多内存,且后面全部都remove了,然后一直占用这块内存,因为Entry数组是不存在缩容的。因此,得出两个结论,结论1:前n次,set次数越多,remove次数越少,就说明扩容次数越多,有可能会导致数组内存的浪费。结论2:采用二分法观察,每次分出来的次数块中的set次数和remove次数持平,可以继续做二分,因此二分的次数越多,说明扩容次数越少,内存越节省。其实可以通过生活中的例子对这类现象做类比,例如高铁,高铁的每一个站都有人上有人下,所以它的容量可以保持一个恒态,容量不会太高,如果像情景1一样,在第1站就有大批人上,请问是否需要加车厢去扩容,扩容之后等到了第2站,所以人全部下车,假设中途没人上车了,是不是就造成了资源浪费了。因此对于这个问题,官方其实也没有做出任何的解决方案,只能让我们使用大量ThreadLocal的去向场景2的趋势接近。
点赞 0
评论 0
全部评论
推荐
最新
楼层
网易互娱
校招火热招聘中
官网直投
相关推荐
林陌苡荼
昨天 21:30
后端
我觉得腾讯的绿牌日常实习还是值得去的
毕竟日常实习也是实习,能学到东西能写上简历,后续无论是求职其他公司还是参加腾讯的暑期实习招聘、校招,也是有优势的。出于保密需求,腾讯内部有时候信息壁垒非常高,因此在蓝绿之间一般绿牌负责更琐碎和打杂的工作,蓝牌相对更接近业务中心。不过日常实习蓝绿牌的差别真的不大,有时候组里急需实习生,就会走绿牌。绿牌稍微工资低一点,权限方面会严一点,没有转正答辩,但如果表现的好,可以和leader谈转蓝牌的事。含金量的话,你不说是没人知道你干的是绿牌,面试的时候人家也只会问你做过什么事,结果怎么样。日常实习其实也有走蓝牌的,只是流程要走非常久,有时候组里急需实习生,就会走绿牌。绿牌稍微工资低一点,权限方面会严一...
投递腾讯等公司7个岗位 >
点赞
评论
收藏
转发
老天赐我一枚offer
05-15 22:34
康冠科技KTC_人力资源(准入职员工)
康冠科技内推
5月还有HC!使用内推码简历优先筛选,有任何问题包括进度查询可以私信我,内推后在评论区留言【姓名缩写+岗位】,方便捞人和确认投递状态🎓【校招对象】2024届毕业生(含2023届毕业未就业学生)💼【在招职位】算法类、软件类、硬件类、结构/光学类、产品类、设计类、技术类、职能类、商务类、供应链类、米哈游,十大类共计600+岗位需求。💰【薪资待遇】本科生年薪12万-21万,硕士年薪15万-24万,博士年薪35万起,每年至少两次调薪机会。🌏【工作地点】深圳、惠州📮【网申地址】https://careerktc.zhiye.com/campus🎯【内推码】ESVM91投递的uu留言下姓名缩写...
投递康冠科技KTC等公司6个岗位 >
点赞
评论
收藏
转发
来世不做开发
05-14 14:19
泉州信息工程学院 计算机类
boss招聘
投了500份,第一次遇到这样的,明明期望也有测试😅
点赞
评论
收藏
转发
陈浩锋
04-16 18:04
东莞城市学院 计算机类
招牛马
怎么问身份啥的那么多,实力方面不问?
点赞
评论
收藏
转发
卷山羊
05-16 17:49
门头沟学院 计算机类
Offer比较
后端(南京)总包比测开(北京)低3w,非北京人没户口
点赞
评论
收藏
转发
点赞
收藏
评论
分享
回复帖子
提到的真题
返回内容
全站热榜
1
...
开摆了,写小说去了
9408
2
...
没offer的我们也很优秀偶
9041
3
...
【有奖活动】浅聊一下我的实习⭐
8891
4
...
双非本 腾讯WXG暑期已offer | 附面经
8741
5
...
因为找实习和女朋友分手了
8739
6
...
华为暑期开奖
6556
7
...
帮助大家答疑
5950
8
...
华为实习offer!终于告一段落了
5584
9
...
真有必要读研吗
5397
10
...
滴滴秋储-服务端开发 OC
5360
正在热议
#
牛客帮帮团来啦!有问必答
#
826031次浏览
13064人参与
#
机械制造薪资爆料
#
320530次浏览
3736人参与
#
晒一晒我的offer
#
3472535次浏览
55291人参与
#
0offer是寒冬太冷还是我太菜
#
428620次浏览
4949人参与
#
金三银四,你有感觉到吗
#
329732次浏览
4227人参与
#
海康威视求职进展汇总
#
101826次浏览
1217人参与
#
我在牛爱网找对象
#
50342次浏览
328人参与
#
实习生应该准时下班吗
#
80948次浏览
595人参与
#
软件开发投递记录
#
479472次浏览
7244人参与
#
如果可以选,你最想从事什么工作
#
186409次浏览
3082人参与
#
求职遇到的搞笑事件
#
19735次浏览
287人参与
#
实习必须要去大厂吗?
#
13964次浏览
222人参与
#
你觉得找工作该拿大厂还是小厂练手
#
61800次浏览
873人参与
#
荣耀求职进展汇总
#
70896次浏览
719人参与
#
你的秋招进行到哪一步了
#
368915次浏览
6400人参与
#
国企vs私企,你更想去?
#
20296次浏览
205人参与
#
你觉得通信/硬件有必要实习吗?
#
23645次浏览
428人参与
#
你觉得今年秋招难吗
#
311606次浏览
5792人参与
#
正在春招的你,也参与了去年秋招吗?
#
136547次浏览
1707人参与
#
想实习转正,又想准备秋招,我该怎么办
#
118146次浏览
1335人参与
牛客网
牛客企业服务