ThreadLocal从源码角度看执行过程

ThreadLocal源码解析过程

我们来看看set过程的源码

public void set(T value) {
  	//获取线程t
    Thread t = Thread.currentThread();
  	//根据线程来获取对应的ThreadLoaclMap
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

  ThreadLocalMap getMap(Thread t) {
    		//这里是拿Thread类里面的ThreadLocal.ThreadLocalMap类型
        return t.threadLocals;
    }

接下来看一下如果map为空,走了createMap方法

这里新建了一个ThreadLocalMap来赋值给线程t里面的值,也就是上面的getMap中t.threadLocals

image-20220621222422684

ThreadMap往下走,看到一个构造函数,里面是初始化一些变量,

INITIAL_CAPACITY 数组默认值16
int i 这个是通过hash找到桶的下标
size 桶的数量,这里设置为1
setThreshold是当前桶到达数量多少时的扩容阈值 是桶长度的2/3,16*2/3=10,也就是到达10的时候就要扩容了

image-20220621222613791

image-20220621223043416

分析完了创建,接下来看一下如果存在map具体逻辑是怎么做的

这里我们新建多一个,set值进去

image-20220621223331717

image-20220621223356930

很明显已经进入到了map.set()方法了,往下继续走

image-20220621223714115

tab拿到整个桶,len桶长度,i是hash桶下标

for循环里面,先判断桶下标是否是null,如果不为null,就进入循环,很明显我们这次的桶是null的,如果不想为null,可以设置同一个threadLocal的value

接下来我们往下走

image-20220621224144984

这里发现在new 一个桶,放到我们数组hash得到的数组下,长度+1,重点来了,!cleanSomeSlots(i, sz) && sz >= threshold,这是判断长度是否达到阈值,初始时的阈值是10,如果大于就扩容,那有小伙伴就问了,这个cleanSomeSlots是什么来的,别急,我们继续往下走

image-20220621225106938

这里的i实际是桶的下标位置,从while中是通过n来进行条件判断的说明n就是用来控制扫描趟数(循环次数),这里先判断桶下标的下一个位置是否有值,如果有值,不为null,并且get()出来的值为null,那就说明是脏的entry桶,这个时候进入清除阶段。

这里为什么要做这种判断呢?

这是为了避免发生内存泄漏,防止线程迟迟没结束,根可达分析,gc迟迟未回收,这里的n如果检测到了脏enty之后,就把n=len,主要是扩大扫描的次数,这样扫描的范围也会增大,从而检测到更多的脏entry。

接下来我们来看看expungeStaleEntry是如果工作的?

image-20220621230240091

设置脏桶的value为null,和桶数组null,长度减1,清除完了当前的脏痛之后,继续往下扫描,直到tab[i]==null的时候停止,

为什么这个时候停止呢?

原因是脏桶的定义是key为null,桶不为null才算脏桶,所以当为null的时候就退出了

如果往下查找过程中又发现k==null,也就是脏桶,这个时候跟上面一样清除

那下面的else是干嘛的呢?

这里其实是处理rehash情况下,如果这个时候发生rehash的情况,往下找不为null的,如果找到了,放上去。

至此,整个set正常流程已经结束了,那还有非正常流程吗?有的,刚才我们看到

image-20220621232318633

image-20220621233139299

这里其实做了两件事,第一,向前查找脏桶,第二向后查找脏桶

image-20220622000739728

1.向前找到脏桶

img

向后查找发现后有key可以替换,覆盖i桶位置,并且交换i和当前脏桶位置,cleanSomeSlots位置到前脏桶位置上

img

向后没找到可以替换的,插入到当前脏桶位置,cleanSomeSlots位置到前脏桶位置上

2.向前没有脏桶

img

向后查找发现脏桶,并且后有key可以替换,覆盖i桶位置,并且交换i和当前脏桶位置,cleanSomeSlots放到后查询脏桶位置

img

向后没找到可以替换的,插入到当前脏桶位置,cleanSomeSlots位置到前脏桶位置上

至此整个流程结束

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadLocal源码Java中一个关键的类,它提供了一种在多线程环境下实现线程本地变量的机制。在JDK 8之前和之后,ThreadLocal的内部结构有所变化。ThreadLocal源码分为两部分:ThreadLocal类和ThreadLocalMap类。 ThreadLocal类是一个泛型类,它包含了两个核心方法:set()和get()。set()方法用于将一个值与当前线程关联起来,get()方法用于获取当前线程关联的值。 ThreadLocalMap类是ThreadLocal的内部类,它用于存储每个线程的本地变量。在JDK 8之前,ThreadLocalMap是通过线性探测法解决哈希冲突的,每个ThreadLocal对象都对应一个Entry对象,Entry对象包含了ThreadLocal对象和与之关联的值[2]。 在JDK 8之后,ThreadLocalMap的实现方式发生了改变。使用了类似于HashMap的方式,采用了分段锁的机制来提高并发性能。每个线程维护一个ThreadLocalMap对象,其中的Entry对象也是采用链表的形式来解决哈希冲突。 总结起来,ThreadLocal源码主要由ThreadLocal类和ThreadLocalMap类组成。ThreadLocal类提供了set()和get()方法来管理线程本地变量,而ThreadLocalMap类则负责存储每个线程的本地变量,并解决哈希冲突的问题。 史上最全ThreadLocal 详解 ThreadLocal源码分析_02 内核(ThreadLocalMap) 【JDK源码】线程系列之ThreadLocal 深挖ThreadLocal ThreadLocal原理及内存泄露预防 ThreadLocal原理详解——终于弄明白了ThreadLocal ThreadLocal使用与原理 史上最全ThreadLocal 详解。 ThreadLocal源码分析,主要有ThreadLocal源码以及ThreadLocal的内部结构在jdk8前后的变化。 使用方式非常简单,核心就两个方法set/get public class TestThreadLocal { private static final ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { try { threadLocal.set("aaa"); Thread.sleep(500); System.out.println("threadA:" threadLocal.get()); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { threadLocal.set("bbb"); System.out.println("threadB:" threadLocal.get()); } }).start(); } }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值