【线程本地变量ThreadLocal—底层存储结构与内存泄漏探索与分析】

一、初识线程本地变量ThreadLocal

1.1、ThreadLocal是什么

ThreadLocal是每个线程单独特有的一个本地变量,用于存放当前线程产生数据的副本,存储到这个变量中的数据可跨方法以及跨服务进行传递,例如:Spring中事务的控制,使用了ThreadLocal确保了当前操作都是属于同一个数据库连接,不然会导致事务失效,再例如:在使用MDC日志追踪框架时,其实也是用到ThreadLocal进行操作

1.2、ThreadLocal内部存储数据结构

在Thread类中,定义的一个ThreadLocalMap集合容器:
在这里插入图片描述

接着点进ThreadLocal中会发现,这个Map维护了一个Entry数组,每个Entry(且ThreadLocal被弱引用关键字修饰)是以ThreadLocal对象为key,要存储的值为value:
在这里插入图片描述

所以,结合上面的代码图,我们可以大致的去构思出ThreadLocal内部结构图示:
在这里插入图片描述

1.3、ThreadLocal使用

ThreadLocal类接口中有几个方法,如下:

  • **void set(Object value) **:设置当前线程的线程局部变量的值;
  • public Object get():返回当前线程所对应的线程局部变量;
  • public void remove():将当前的线程对应的局部变量移除,目的是为了减少内存的占用;
  • protected Object initialValue():该方法是返回当前线程对应的线程局部变量的初始值,是一个被protect修饰的方法,目的是为让子类可重写覆盖,这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null

1.4、ThreadLocal内部结构hash冲突解决方案

解决hash冲突发主要有以下几种:

  1. 链地址法:这种方法是hashMap采用的方法,如果出现hash冲突,直接在链表后插入即可,如下图:
    在这里插入图片描述
  2. 再has法:这种方法是同时构造多个不同的哈希函数:Hi=RH1(key) i=1,2,…,k当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。
  3. 开放寻址发
    3.1、线性探测再散列:如果当前hash到的位置以及有元素的,再往紧接着的下一个进行判断,以此类推,ThreadLocal是采用的这种方式来解决hash冲突,如下图示:
    在这里插入图片描述
    3.2、二次探测再散列:如果当前hash到的位置以及有元素的,再往1、2、3次方位置进行判断,以此类推,如下图示:
    在这里插入图片描述
    3.3、伪随机:顾名思义就是随机产生一个增量位移,如下图:
    在这里插入图片描述

二、ThreadLocal产生内存泄漏分析

内部结构图,我们可以大致构思出对象在堆和栈上的分布情况,如下图示:
在这里插入图片描述
如上图示,如果当前局部的方法执行完毕了,但线程未结束,那ThreadLocal这个局部变量就会出栈,相关的引用也随之去,那堆中,创建出的ThreadLocal对象只有Entry的弱引用指向了,如果发生GC,那ThreadLocal对象是直接被收回的,这时,Entry对象的key就会为null,那这个数据就无法访问了,就属于垃圾对象了,特别是线程池环境下,越积越多,就产生了内存泄漏,解决方案其实就是在局部方法执行完毕之后,记得调用当前ThreadLocal对应的remove方法即可;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值