ThreadLocal源码浅析

ThreadLocal俗称线程本地, 顾名思义,它提供的是封闭在线程的局部变量,也就是说只有当前线程才能设置、获取、移除等操作,并且它将随着线程的消亡而被GC。

原理图

查看源码之前,我们先看一个图

如图所示,每一个Thread线程对象中包含了一个ThreadLocalMap,这是一个简化版的hashmap的实现。所以自然的,ThreadLocalMap包含着一个散列表。散列表存放的是key/value形式的数据。key是ThreadLocal实例,value就是你要设置的数据。

当前你通过ThreadLocal去设置数据时,如:

threadLocal.set("lay");

1、根据threadLocal中包含的一个hashCode(标识当前threadLocal实例),计算出落在散列表上的索引位置;

2、然后把threadLocal作为key + 要存储的数据一起存储到散列表的节点上;

3、如果该节点已经存在数据了,那么往右找下一个节点,如果右边没有节点了,那么循环到索引为0的位置,以此类推;

4、如果没有充足的位置存放数据了,那么将进行扩容,并重新散列。

ThreadLocalMap是被组合在Thread对象里的,所以当线程消亡的时候ThreadLocalMap将失去引用,从而被GC,而ThreadLocalMap里面存放的数据,如果没有其它引用持有的话那么也一样会被GC回收。并且,外部无法直接访问ThreadLocalMap,而ThreadLocal类和Thread在同一个包下因而你仅可以通过它来访问,从而保证了ThreadLocalMap封闭在线程内部。

DEMO

我们从一个简单的DEMO入手

源码解析

 首先构造了一个ThreadLocal静态变量,构造方法里面啥也没干

set()

点击进入set()方法

先获取了当前线程Thread对象,然后从Thread对象里面获取ThreadLocalMap对象。初次调用的话ThreadLocalMap为空,那么进入createMap方法创建一个。

进createMap看看怎么创建的

构造一个ThreadLocalMap的时候,把当前ThreadLocal的实例传入,并把值也传入,看看构造方法

构造方法先初始化了一个空的hash表,然后根据threadLocal的hashCode计算出索引的位置,再以threadLocal作为key创建了一个节点(Entry),设置大小为1,并设置扩容的阈值。这样一个初始化的ThreadLocalMap就构建完了。

我们回到set的时候判断ThreadLocalMap是否为空上来,如果它不为空直接调用ThreadLocalMap的set方法设置值。进入set方法

set方法有点长,原理比较简单。就是先根据threadLocal的hashCode计算出索引位置,然后比对threadLocal对象是不是当前这个对象,遍历整个hash表直到找到对象,或者完全找不到对象。前者直接赋值,后者创建一个节点。创建节点的话会判断需不需要扩容,如果需要的话重新进行hash计算。

get()

看完set方法,再看get方法就显得很简单了

一样是先拿到Thread中的ThreadLocalMap,如果Map为空,那么返回一个初始值。如果不为空,找到Entry节点,如果节点找了返回节点数据,否则返回初始值。

remove()

remove方法,一样很简单

找到ThreadLocalMap,调用remove方法,传入threadLocal实例。进入remove看看

遍历hash表,找到该key对象的Entry,然后做了清理操作。

总结

 ThreadLocal实际上是包含了ThreadLocalMap的数据操作,而ThreadLocalMap组合在Thread中,随着Thread消亡。

 

转载于:https://www.cnblogs.com/lay2017/p/11058285.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值