源码分析--ThreadLocal(图解)
共 8997字,需浏览 18分钟
·
2021-04-17 19:13
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
想清楚ThreadLocal源码的原理,那么先搞明白 ThreadLocalMap ,很关键
1.首先我们知道Thread 里面有一个ThreadLocalMap ;
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
}
2.ThreadLocalMap里面有包含了一个成员变量Entry[] table。
static class ThreadLocalMap {
private Entry[] table;
//Entry继承了WeakReference<ThreadLocal<?>>, 说明Entry 持有一个指向ThreadLocal的弱引用。
//弱引用,就是如果一个对象只有弱引用指向它,下一次JVM垃圾回收的时候一定会被回收调。
static class Entry extends WeakReference<ThreadLocal<?>> {
//这个value就是存放我们的数据
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
///...省略部分源码
}
上面的1,2代码反应了如下关系
一个Thread里面包含了一个ThreadLocalMap
一个ThreadLocalMap包含了一个table
table是一个Entry数组
Entry数组有一个value存放数据,reference弱引用可以指向某个ThreadLocal
3、ThreadLocalMap包含了一个Entry[] table, 其实ThreadLocalMap是底层数据结构就是一个Entry数组。
ThreadLocalMap是一个Map,Entry代表一个哈希槽。
Entry的key(键)其实就是reference, 而value(值)就是上面的value.
ThreadLocalMap存值原理:
1.就是通过Entry的 key的Hash值计算出Index
2.找到数组的Index位置,如果该位置为空就存放Entry
3.不为空则Index ++,直到找到一个空的位置存放 (这里还有一个扩容的问题,暂不讨论)
ThreadLocalMap取值原理:
1.就是通过 key的Hash值计算出Index
2.找到Index的位置Entry,再对比一下Entry的key和需要查找的key是不是相等,相等则取出value
3.不相等则Index++,然后重复第2步骤
总结:每个Thread 对象里面有一个ThreadLocalMap。ThreadLocalMap是一个Map, key是ThreadLocal类型的数据, value存放数据
4.TheadLocal 的set方法
public class ThreadLocal<T> {
public void set(T value) {
//1.获取当前线程的ThreadLocalMap(就是上面说的ThreadLocalMap)
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
// ThreadLocalMap是一个Map, key是ThreadLocal类型的数据, value存放数据
// 2.map 不为空, Map的key是this对象本身,value是我们存放的数据。
//可能会疑惑,为什么key存放this?下面再详细说明
map.set(this, value);
else
//3.map 为空,为当前线程创建一个Map
createMap(t, value);
}
}
源码为什么key存放this?
1.一个Thread有一个ThreadLocalMap,而ThreadLocalMap可以存放多个Entry { “key”:ThreadLocal :“value”: object }
2.如下测试代码所示,当我们在线程A里面操作threadLocal1.set(1),
其实就是在线程A自己的ThreadLocalMap的 Entry{ “key”:threadLocal1 :“value”: 1}。
反之, 线程B就是线程B自己的ThreadLocalMap 的 Entry{ “key”:threadLocal1 :“value”: 2}。
public class ThreadLocalTest {
static ThreadLocal<Integer> threadLocal1 = new ThreadLocal<>();
static ThreadLocal<Integer> threadLocal2 = new ThreadLocal<>();
public static void main(String[] args) {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try{
threadLocal1.set(1);
threadLocal2.set(2);
}finally {
threadLocal1.remove();
threadLocal2.remove();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try{
threadLocal1.set(1);
threadLocal2.set(2);
}finally {
threadLocal1.remove();
threadLocal2.remove();
}
}
});
threadA.start();
threadB.start();
}
}
最后数据存放图如下(table里面entry存放数据的位置只是假设,threadLocal1 存放的位置还是通过它的Hash值求出的Index所得 ):
5.ThreadLocal的get方法( 如果看懂上面的set方法,这里就很简单了)
public T get() {
//1.获取当前线程的ThreadLocalMap(就是上面说的ThreadLocalMap)
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//map 不为空,根据this获取Entry
ThreadLocalMap.Entry e = map.getEntry(this);
//e不为空,返回e.value
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
//initialValue() 就是返回了一个null
T value = initialValue();
Thread t = Thread.currentThread();
//获取当前线程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//map 不为空,设置值为null
map.set(this, value);
else
//map 为空,创建一个map,并设置值为null
createMap(t, value);
return value;
}
6.ThreadLocal的数据用完后记得使用 threadLocal.remove()移除数据,不然在某种情况下可能会导致内存泄漏(如图下描述)
————————————————
版权声明:本文为CSDN博主「略。。。。」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:
https://blog.csdn.net/fsdf8sad7/article/details/113939691
锋哥最新SpringCloud分布式电商秒杀课程发布
👇👇👇
👆长按上方微信二维码 2 秒
感谢点赞支持下哈