Java基础知识11

本文详细介绍了Java中的ThreadLocal类,包括其使用方法、应用场景、原理、内存泄漏原因及解决策略,以及ThreadLocalMap的结构、冲突解决和扩容机制。同时提到了InheritableThreadLocal如何支持父子线程间的数据共享。

ThreadLocal

简介

ThreadLocal是Java中提供的实现线程局部变量的工具类,允许每个线程都拥有自己的独立副本,从而实现线程隔离,用于解决多线程中共享对象的线程安全问题,其图示如下。

使用方法

1、创建ThreadLocal变量,需指定泛型类型,代码如下:

public static ThreadLocal<String> lt = new ThreadLocal<> ();

2、对于创建的变量,调用set方法赋值。

3、调用get方法获取ThreadLocal的值。

其结构体如下:

使用场景

1、存储用户信息,在控制层拦截时可将用户信息存储到ThreadLocal中,在需要时随时取出即可;

2、数据库连接池,将数据库连接池的连接交给ThreadLocal进行管理,能够保证当前线程的操作都是同一个Connnection。

ThreadLocal原理

1、Thread 类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals,每个线程都有一个属于自己的ThreadLocalMap。

2、ThreadLocalMap内部维护着Entry数组,每个Entry是一个完整的对象,key是ThreadLocal 的弱引用,value是ThreadLocal的泛型值。

3、每个线程在往ThreadLocal里设置值的时候,都是往自己的ThreadLocalMap里存,调用set方法,通过getMap方式,传入当前线程为key,获取map,从而实现了线程隔离,读也是以某个ThreadLocal作为引用。

4、栈存储Thread和ThreadLocal的引用,堆存储他们的示例值。

5、ThreadLocal本身不存储值,它只是作为一个key来让线程往ThreadLocalMap里存取值,其原理图如下。

ThreadLocal发现内存泄漏的原因

1、ThreadLocal生命周期过长,若其变量在使用后未被及时清理,则ThreadLocalMap中的键值对变量一直存在,当外部没有对该对象的引用时,会出现无法回收的情况。

2、如果一个ThreadLocal对象已经不再被使用,但是线程仍然在运行,并且其ThreadLocalMap中还保留着对这个ThreadLocal对象的键的引用,这会导致ThreadLocal对象所引用的数据也无法被回收,因为ThreadLocalMap中的键是对ThreadLocal对象的弱引用,但值是强引用。

PS:

强引用:最传统的引用,无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象,如Object o = new Object();

弱引用:被弱引用关联的对象只能生存到下一次垃圾收集发生时,无论当前内存是否足够,弱引用对象都会被回收。

解决方法:用完后调用remove方法。

ThreadLocalMap中,Entry 的key为弱引用,意味着当 ThreadLocal 外部强引用被置为null(ThreadLocalInstance=null)时,根据可达性分析,ThreadLocal实例此时没有任何一条链路引用它,所以系统GC的时候 ThreadLocal 会被回收。

ThreadLocalMap结构

无接口,采用元素数组存储元素,并采用散列方法把key映射到数组的对应下标。

ThreadLocalMap解决冲突的方法

使用开放地址法,在获取元素时,会根据 ThreadLocal 对象的 hash 值,定位到 table 中的位置,然后判断该槽位Entry对象中的key是否和get的key一致,如果不一致,就判断下一个位置。

ThreadLocalMap扩容机制

1、在 ThreadLocalMap.set()方法的最后,如果执行完启发式清理工作后,未清理到任何数据,且当前散列数组中Entry的数量已经达到了列表的扩容阈值(长度len的3分之2),就开始执行rehash方法。

2、rehash会先去清理过期的Entry,然后还要根据条件判断容量是否大于等于阈值的4分之3也就是来决定是否需要扩容,若满足则扩容。

3、通过resize()方法扩容,扩容后新数组的大小为老数组的两倍,然后遍历老的数组,散列方法重新计算位置,开放地址解决冲突,然后放到新的数组,遍历完成之后,老数组中所有的entry数据都已经放入到新的数组中了,然后table引用指向新数组,其代码如下:

ThreadLocal父子线程共享数据

通过InheritableThreadLocal进行,主线程在InheritableThreadLocal赋值,子线程直接调用get拿到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值