ThreadLocal源码解析

ThreadLocal通过ThreadLocalMap存储线程局部变量,使用弱引用来避免内存泄漏,同时采用开放地址法处理哈希冲突,初始化和获取值时有特定的策略。ThreadLocalMap在键值对中使用弱引用避免了当ThreadLocal对象不再被引用时,其在Map中的键仍然存活导致的内存泄露问题。
摘要由CSDN通过智能技术生成

  1. 整体架构

ThreadLocal中除了有几个基本的属性和方法外,真实的get、set、remove逻辑都放在静态内部类ThreadLocalMap中

1.1 属性

  • threadLocalHashCode:当前ThreadLocal对象的哈希值

  • nextHashCode:下一个哈希值,AtomicInteger类型

  • HASH_INCREMENT:步长,保障不会出现哈希冲突

1.2 常用方法

nextHashCode():按照步长为属性赋哈希值,ThreadLocal解决hash冲突的方法是开放地址法,如果出现hash冲突就按照一定的步长寻找下一个位置,这个方法就是用来获取下一个位置的。

initialValue()、setInitialValue():在用户刚创建ThreadLocal对象且未set值的之前若调用get方法,会使用这两个方法, 用户可以重写initialValue()方法来自己设置初始值

get():map为null则获取初始值,否则从map中获取

set(T value):给当前threadLocal对象设置值,懒加载ThreadLocalMap对象。另外我们可以看到获取ThreadLocalMap时是通过当前线程获取的,当前线程的threadLocals属性就是ThreadLocalMap对象,由此我们可以知道ThreadLocalMap是每个线程一个副本,每个线程下可以有多个ThreadLocal对象,获取时是从当前线程下的ThreadLocalMap中根据ThreadLocal对象的hash值来获取,存储时也同理。

  1. ThreadLocalMap

2.1核心数据

TheadLocal中的数据就存储在这个Entry数组中

2.2静态内部类Entry

TheadLocalMap并没有直接使用原生的Entry,而是继承了弱引用接口,在这个Entry的构造方法中,它将k设置为弱引用。为什么要将key设置为弱引用,我们知道在代码中我们使用threadLocal对象时是强引用,map里的key也指向这个threadLocal对象,这样如果这个强引用不消失,那么map里的key就也不会消失,同样的,如果threadLocal对象不被使用,即当前方法栈中不再引用threadLocal对象时,强引用消失,threadLocal对象和map里的key都会在经过下次垃圾回收时自动就会变成null。那么如果key是强引用会发生什么情况呢?即使方法栈中的引用不再指向threadLocal对象,map中的key还是会强引用的指向它,那么就不会自动清除,只有当前线程结束时才会清除整个map集合,如果当前线程不结束,那么随着时间的推移,可能会发生内存溢出的情况。

2.3TheadLocalMap如何防止Hash冲突

先看源码,这是TheadLocalMap中的set方法,大致分为两部分,上面的红框是一个for循环,看里面的判断可以知道,如果key相同就替换value,如果key为Null表明这个弱引用的threadLocal对象可能已经被垃圾回收,清理旧值以后赋上新值。循环条件就是防止Hash冲突的精髓,如果key既不相等也不为空,表明这个位置上已经有了一个key不相同的值,已经被占用,这时候的会跳到下一个循环,调用nextIndex()方法,根据一定的步长到下一个位置继续判断,直到找到一个空的位置。下面第二个红框则是循环结束后找到合适位置时将value存入的方法,存入后会执行清除方法,清除那些key为空的节点。

最后讲一下int i = key.threadLocalHashCode & (len-1)这一行代码,这行代码的含义是获取数据中key的位置,我们知道这个数组的长度初始值为16,扩容也是按照2的倍数来扩容的,那么len-1的二进制值的最后一个字节一定是1111,&符号只有两边都是1的时候结果才为1,例如1&15,前面是0000 0000 0000 0001,后面是0000 0000 0000 1111,运算结果为0000 0000 0000 0001,同样的17的二进制为0000 0000 0001 0001,运算结果为

0000 0000 0000 0001,换算为十进制的话也是1,由此我们知道,key.threadLocalHashCode & (len-1)这个表达式的作用是取余,这样取余的方式只有后面 (len-1)为二的n次方时才成立。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值