ThreadLocal源码分析

ThreadLocal是什么

下面引用ThreadLocal的API注释
这个类提供线程局部变量。 这些变量与其正常的对应方式不同,因为访问一个的每个线程(通过其get或set方法)都有自己独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段(例如,用户ID或事务ID)。
例如,下面的类生成每个线程本地的唯一标识符。 线程的ID在第一次调用ThreadId.get()时被分配,并在后续调用中保持不变。
在这里插入图片描述

只要线程存活并且ThreadLocal实例可以访问,每个线程都保存对其线程局部变量副本的隐式引用; 线程消失后,线程本地实例的所有副本都将被垃圾收集(除非存在对这些副本的其他引用)。

ThreadLocal原理

ThreadLocal供线程局部变量。实际上它并不存放任何的信息。简单的说,ThreadLocalMap是ThreadLocal真正的数据存储容器,而ThreadLocalMap实例是Thread的成员变量,如果在同一个线程中使用多个ThreadLocal实例,实际上,每个ThreadLocal实例对应的是ThreadLocalMap的哈希表中的一个哈希槽。
下面画个图说明一下在线程中使用ThreadLocal实例的:
在这里插入图片描述
线程实例和ThreadLocal实例的关系如下:
在这里插入图片描述
ThreadLocal实例总是通过Thread.currentThread()获取到当前操作线程实例,然后去操作线程实例中的ThreadLocalMap类型的成员变量,因此它是一个桥梁,本身不具备存储功能。

ThreadLocal源码分析

ThreadLocal结构

在这里插入图片描述

ThreadLocal构造方法

  • ThreadLocal只有一个无参构造方法,初始化的时候什么也不做。可以猜到在第一次set方法调用的时候会懒初始化一个ThreadLocalMap
    /**
     * 自定义哈希码(仅在 ThreadLocalMaps 中有用)
     * 消除冲突在连续构造 ThreadLocals 的常见情况下被相同的线程使用
     */
    private final int threadLocalHashCode = nextHashCode();

    /**
     * 原子计数器,给出的下一个哈希码
     */
    private static AtomicInteger nextHashCode =
            new AtomicInteger();

    /**
     * //哈希魔数
     */
    private static final int HASH_INCREMENT = 0x61c88647;

    /**
     * 创建一个线程局部变量
     *
     */
    public ThreadLocal() {
    }

ThreadLocal内部类ThreadLocalMap结构

  • 在下面的代码中,我们可以看出,当前ThreadLocal的引用k被传递给WeakReference的构造函数,所以ThreadLocalMap中的key为ThreadLocal的弱引用。当一个线程调用ThreadLocal的set方法设置变量的时候,当前线程的ThreadLocalMap就会存放一个记录,这个记录的key值为ThreadLocal的弱引用,value就是通过set设置的值。如果当前线程一直存在且没有调用该ThreadLocal的remove方法,当没有强引用来引用ThreadLocal实例的时候,JVM的GC会回收ThreadLocalMap中的这些Key,此时,ThreadLocalMap中会出现一些Key为null,但是Value不为null的Entry项,这些Entry项如果不主动清理,就会一直驻留在ThreadLocalMap中
    /**
     * ThreadLocalMap是一个定制的散列映射,仅适用于维护线程本地变量。
     * 它的所有方法都是定义在ThreadLocal类之内。
     * 它是包私有的,所以在Thread类中可以定义ThreadLocalMap作为变量。
     * 为了处理非常大(指的是值)和长时间的用途,哈希表的Key使用了弱引用(WeakReferences)。
     * 引用的队列(弱引用)不再被使用的时候,对应的过期的条目就能通过主动删除移出哈希表。
     */
    static class ThreadLocalMap {

        /**
         * Entry的Key为WeakReference<ThreadLocal<?>>
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /**
             * 这里是正在存放ThreadLocal.set()的值.
             */
            Object value;

            /**
             * k:ThreadLocal的引用,被传递给WeakReference的构造方法
             */
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        /**
         * 初始容量,必须是2的幂
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * 哈希(Entry)表,必须时扩容,长度必须为2的幂次方
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private ThreadLocalMap.Entry[] table;

        /**
         * 哈希表中元素(Entry)的个数
         * The number of entries in the table.
         */
        private int size = 0;

        /**
         * 下一次需要扩容的阈值,默认值为0
         * The next size value at which to resize.
         */
        private int threshold; // Default to 0

        /**
         * 设置下一次需要扩容的阈值,设置值为输入值len的三分之二
         * Set the resize threshold to maintain at worst a 2/3 load factor.
         */
        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }

        /**
         * 以len为模增加i
         * Increment i modulo len.
         */
        private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }

        /**
         * 以len为模减少i
         * Decrement i modulo len.
         */
        private static int prevIndex(int i, int len) {
            return ((i - 1 >= 0) ? i - 1 : len - 1);
        }
 }

TreadLocal的set方法

    public void set(T value) {
        //获取当前线程实例
        Thread t = Thread.currentThread();
        //从当前线程实例中获取threadLocals属性 
        ThreadLocalMap map = getMap(t);
        if (map != null)
            //threadLocals属性不为null则覆盖key为当前的ThreadLocal实例,值为value
            map.set(this, value);
        else
            //threadLocals属性为null,则创建ThreadLocalMap,第一个项的Key为当前的ThreadLocal实例,值为value
            createMap(t, value);
    }
  • 在上面的代码中,处调用getMap方法获得当前线程对应的threadLocals
    //获取ThreadLocalMap实例时候总是从线程实例的成员变量获取
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
  • 如果getMap方法返回null说明是第一次调用set方法(前面说到过,threadLocals默认值为null,只有调用set方法的时候才会创建map),这个时候就需要调用createMap方法创建threadLocals,该方法如下所示
    // 创建ThreadLocalMap实例的时候,会把新实例赋值到线程实例的threadLocals成员
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

TreadLocal的get方法

  • 在get方法的实现中,首先获取当前线程实例,如果当前线程的threadLocals不为null,就直接返回当前线程绑定的本地变量值,否则执行setInitialValue方法初始化threadLocals变量。在setInitialValue方法中,类似于set方法的实现,都是判断当前线程的threadLocals变量是否为null,是则添加本地变量(这个时候由于是初始化,所以添加的值为null),否则创建threadLocals变量,同样添加的值为null。
public T get() {
    //获取当前线程实例
    Thread t = Thread.currentThread();
    //获取当前线程的threadLocals变量
    ThreadLocalMap map = getMap(t);
    //如果threadLocals变量不为null,就可以在map中查找到本地变量的值
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    //执行到此处,threadLocals为null,调用该更改初始化当前线程的threadLocals变量
    return setInitialValue();
}

private T setInitialValue() {
    //protected T initialValue() {return null;}
    T value = initialValue();
    //获取当前线程
    Thread t = Thread.currentThread();
    //以当前线程作为key值,去查找对应的线程变量,找到对应的map
    ThreadLocalMap map = getMap(t);
    //如果map不为null,就直接添加本地变量,key为当前线程,值为添加的本地变量值
    if (map != null)
        map.set(this, value);
    //如果map为null,说明首次添加,需要首先创建出对应的map
    else
        createMap(t, value);
    return value;
}

TreadLocal的remove方法

  • remove方法判断该当前线程对应的threadLocals变量是否为null,不为null就直接删除当前线程中指定的threadLocals变量
public void remove() {
    //获取当前线程绑定的threadLocals
     ThreadLocalMap m = getMap(Thread.currentThread());
     //如果map不为null,就移除当前线程中指定ThreadLocal实例的本地变量
     if (m != null)
         m.remove(this);
 }

总结

感谢您的阅读

如果你发现了错误的地方,可以在留言区提出来,我对其加以修改

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值