源码分析--ThreadLocal(图解)

Java资料站

共 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 秒





感谢点赞支持下哈 

浏览 12
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报