ThreadLocal源码分析

为什么ThreadLocal能在自己的线程中保持独立的副本呢,其实在Thread类中有这么一个属性,看下图:
在这里插入图片描述
里面维护了一个threadlocals的属性,这个是一个本质其实就是一个entry数组
在这里插入图片描述

看上图可知,Entry是一个弱引用,易在GC的时候被回收,其中这个map中key就是我们的threadlocal对象,value就为我们需要保存的副本变量。
在这里插入图片描述

首先ThreadLocal中上面的截图,是关联计算再数组中下标的位置。这里采用的是斐波那契数,使得hash均匀的分配。在这里采用concurrent包下的原子类通过cas原理没创建一个新的threadlocal再次相加一遍斐波那契数。
在这里调用threadlocal的set方法也特别简单:
在这里插入图片描述
大致逻辑就是先获取当前线程的那个存取副本变量的map,map存在set,不存在就创建,我们这里主要看一下map的set内部的实现。
在这里插入图片描述
首先获取Entry数组以及计算数据应该落在数组的哪个下标位置。然后获取对应数组中桶的对象也就是Entry对象。
这里的逻辑分两种:

  1. 获取对应的桶为空,那么直接创建一个桶,将key,value放入桶中,保存在计算出来的数组位置。
  2. 获取对应的桶不为空,那么先获取桶中的key,若需要插入的key与现在数组中获取到的桶的key相同,那么直接替换对应桶中value。若key为null,说明此时对应桶的key已经失效,需要对数组进行失效桶的整理删除再把对应的key,value放入桶中,在放入对应的数组位置。那我们就看一下replaceStaleEntry这个方法。

在这里插入图片描述
在这里插入图片描述可以看到方法中定义了一个变量slotToExpunge,一开始值为现在对应的桶key值为null的下标,这个变量其实就是找到桶前面在数组中还存在的过期桶(也就是同对应的key为null)的起始位置,一直向前遍历,直到遇到桶为null的情况。
从方法中的第一个循环的代码逻辑就可以看出来。
看下面的图,也是整个代码逻辑的实现:
在这里插入图片描述
再看方法中循环的这一段:
在这里插入图片描述
看下图

在这里插入图片描述
此时就会从8这一点开始清理工作,我们先看一下expungeStaleEntry这个方法
在这里插入图片描述
在这里插入图片描述

下来再看这个方法
在这里插入图片描述
其实这个方法就是从刚才返回空桶的下标开始,再次遍历,遇到那中过期桶后再继续将过期桶置空,并且调整后面正常桶的桶位,使得其更加靠近本应插入的位置,或者此时插入的位置刚好为空,那么就直接可以调整到正确的位置上。

下来再看一下这个内置的map的扩容机制:
在这里插入图片描述
也就是在红框位置,如果没有清除的桶位并且当前的size大于threshold这个值,threshold这个值在代码中可以看到就是数组总长度的三分之二
在这里插入图片描述
那么进入rehash方法中
在这里插入图片描述
Rehash的时候调用expungeStaleEntries方法
在这里插入图片描述
这里是遍历数组中的每一个桶位,发现了过期桶就去清除,并且重排一些正常桶位如果执行完此方法之后size的大小还是大于或者等于threshold的四分之三,那么就进行扩容操作。
在这里插入图片描述
扩容的时候是在原来的长度的基础上乘以2倍,遍历原来的老数组,得到对应得每一个桶,存在过期桶,那么将过期桶得value置为null,正常桶重新计算hash值得到下标位置,如果桶得位置不为null那么一直向后遍历直到找到空桶位将原来老得桶放入新的数组桶位。

对于为什么会产生过期桶呢?过期桶为什么一定要被回收呢?
因为Entry得本身实现其继承了一个弱引用
在这里插入图片描述
当key对应得threadlocal没有指向任何值得时候,也就是没有强引用时,当发生gc得时候很容易被回收,那么就会造成key被回收,value还在,最后导致内存泄漏得问题。
下面代码可以自己试一下
在这里插入图片描述
在这里插入图片描述

总结一下:
再ThreadLocalMap这个数据结构中,减少hash碰撞的要点有使用斐波那契,使得hash出来的下标均匀分配。
在插入ThreadLocalMap里面的时候,如果计算出来的下标对应桶的位置正好是空桶那么可以直接插入。
当计算出来下标位置上有正常桶,且key值相同那么直接替换对应的value值
当计算出来的下标位置上有过期的桶,也就是key为null的时候,其会在后置循环的时候,找到一个与其对应的key值得桶位,将其value换成副本得value,与现在应该插入得过期桶位交换位置。
在清除过期桶位得方法中,里面包含了让正常桶位重新占位得逻辑,目的就是为了更加靠近或者直接放入正确的桶位中,便于查询。
使得map触发扩容机制得条件有两点,第一点如果当前数组得size大于等于总长度得2/3。
第二点清理后数组得size还是大于等于threshold得3/4,也就是大于等于总长度得1/2,满足上述两个条件就会触发扩容机制。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值