美文网首页
ThreadLocal 深入了解

ThreadLocal 深入了解

作者: 那些年搬过的砖 | 来源:发表于2021-05-10 08:35 被阅读0次

    ThreadLocal用于提供线程局部变量,每个线程有拥有其独立初始化的变量副本。当线程消失时,其线程本地实例的所有副本都将被垃圾回收。

    ThreadLocal<Object> threadLocal = new ThreadLocal<>();
    

    Thread类中定义了类型为ThreadLocal.ThreadLocalMap的成员变量

    /*
         * InheritableThreadLocal values pertaining to this thread. This map is
         * maintained by the InheritableThreadLocal class.
         */
        ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    

    ThreadLocal定义

    static class ThreadLocalMap {
    
            static class Entry extends WeakReference<ThreadLocal<?>> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
    }
    

    内存中关系如下图表示


    通过源码可以看出
    1、new ThreaLocal()会在堆内存中创建一个ThreadLocal对象,对应的应用在栈中。
    2、ThreadLocalMap是Thread类的成员变量,key是ThreadLocal对象,Value是当前线程的局部变量。
    key通过弱引用指向ThreadLocal对象,这里为什么会设计成弱引用(关于弱引用及软引用、强引用的关系参考上一篇文章),是因为ThreadLocal的设计者考虑到线程往往是以线程池的形式存在,这样线程的生命周期就会很长,Thread对象->ThreadLocalMap->Entry这条引用链会一直存在,因为是强引用,GC时不会被回收,如果key强应用ThreadLocal对象,那么key引用的对象也不会被回收,容易造成内存的泄露。而设计成弱引用后,只要ThreadLocal->ThreadLocal对象的这条强引用关系消失,则GC的时候threadlocal对象就会被回收。而ThreadLocal的作用域是当前线程,所以当当前线程结束时,ThreadLocal对象将会在下次GC时被回收掉。

    ThreadLocalMap数据结构

    ThreadLocal的get/set都是通过ThreadLocalMap实现,而ThreadLocalMap内部跟Map类似,也是通过数组实现。数组元素是Entry对象,key值是ThreadLocal对象,value值存放线程变量。

        static class ThreadLocalMap {
            static class Entry extends WeakReference<ThreadLocal<?>> {
                Object value;
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
            //初始容量大小
            private static final int INITIAL_CAPACITY = 16;
    
            //元素存放数组
            private Entry[] table;
    
            //数组元素个数
            private int size = 0;
    
            //扩容阈值
            private int threshold; // Default to 0
    
    ThreadLocalMap Hash冲突解决办法

    我们知道在Map中,如果Hash冲突,会存放为链表结构,当链表长度超过一定阈值时(超过8)会转换为红黑树,但是ThreadLocal不同,即没用链表也没用红黑树,而是采用了线性探测的方式,即先找到需要存放的位置,如果该位置不为空,则根据一定算法计算步长,线性寻址下个位置。


    private void set(ThreadLocal<?> key, Object value) {
    
                Entry[] tab = table;
                int len = tab.length;
                // 计算要存放元素在数组中的位置,上面第一步
                int i = key.threadLocalHashCode & (len-1);
    
                for (Entry e = tab[i];
                     // 循环结束条件是元素为null,即找到存放位置
                     e != null;
                     // 根据固定算法计算循环步长 
                     e = tab[i = nextIndex(i, len)]) {
                    ThreadLocal<?> k = e.get();
                    //key相同,则更新值,并退出循环
                    if (k == key) {
                        e.value = value;
                        return;
                    }
                    //如果key为空,做一些清理操作
                    if (k == null) {
                        replaceStaleEntry(key, value, i);
                        return;
                    }
                }
    
                // 找到合适的槽位,并new一个新的Entry
                tab[i] = new Entry(key, value);
                int sz = ++size;
                //计算是否需要扩容
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
                    rehash();
            }
    

    相关文章

      网友评论

          本文标题:ThreadLocal 深入了解

          本文链接:https://www.haomeiwen.com/subject/okmbdltx.html