源码理解ThreadLocal
目录
首先说一下使用方法。
1、创建ThreadLocal 对象 threadLocal。
2、创建线程 A 、线程 B。
3、在线程 A 和线程 B中 调用 threadLocal.set() get() 函数
常见疑惑:
内存 堆里面只有一个 ThreadLocal,那么一般理解上,线程 A 对 threadLocal 执行 set(value),那么 线程 B 对 threadLocal 执行get 也应该得到 这个value
代码如下:
执行结果如下:
在不同线程中,对同一个对象执行set get方法,结果不同。
源码解析:
简单总结:
线程持有 ThreadLocalMap对象 -- ThreadLocalMap对象内维持一个数组 --- 数组内对象为 Entry --- Entry继承软引用,Key是软引用 --- Entry的key值是 ThreadLocal对象,Value值是 threadLocal.set(Value) 传递的值
一、new ThreadLocal
1、默认构造器是空实现;
2、默认实例变量没有特殊设置;
二、第一次调用set函数
1、切换到当前线程,这一步很重要。
2、获取当前线程实例变量,threadLocalMap;
3、我们进入到 Thread类中,去看一下 threadLocals
整个 Thread类中,只有这一步用到这个对象。默认为空。
4、那么当我们第一次调用函数 set()的时候,map == null;
5、
6、分析 createMap();
这里的this 是 ThreadLocal对象。
这里我们可以看到,给当前线程的实例变量 threadLocals赋值。
7、分析 new ThreadLocalMap
看一下 new Entry
k值作为软引用。
看一下扩容
总结:当第一次调用 set函数时候,动作如下
切换当前线程 --- 获取当前线程 ThreadLocalMap对象 --- 为空的话,先创建ThreadLocalMap对象 --- ThreadLocalMap对象内部维持了一个Entry数组,数组长度默认16,扩容因子 2/3--- threadLocal对象的hashcode 和 15进行与运算 --- new Entry放置到 数组里面。
Entry 的K是软引用。即 ThreadLocal对象可以随时被内存回收。这一条很重要。
三 、第二次调用set();
第二次调用时,线程的实例变量 threadLocals “其类型是ThreadLocalMap”,不为空。
set这里有些复杂,先简单说。
一般流程是黄色部分。
①:通过hashcode 和 tab.len - 1 做与运算,计算出当前Key应该在tab的哪个位置。
②:判断 i 位置 Entry是否为null;i 的计算方式如下
③:不为null,判断key,如果同一 key 则替换 value
④:如果 i 位置 Entry 为null,则新建 Entry,指向 tab[i]
⑤:cleanSomeSlots 判断是否有 key被回收了
最难的部分:
留个坑,下班了