java源码阅读之深入理解ThreadLocal

总结:

  1. 每一个Thread实例中都会保存一个map数据结构,map中的Entry<ThreadLocal,value>用来存储线程中用到的每个ThreadLocal的引用和其对应的value。使用set和get方法时,在Thread实例的map中查找和修改ThreadLocal对应的value值。
  2. 防止内存泄露问题:因为map中的Entry对象存储的是ThreadLocal的弱引用,所以在gc时ThreadLocal有可能被回收,之后其对应的Entry<ThreadLocal, value>中的ThreadLocal就为null,但是Entry却由于存在强引用而不能被回收。所以在使用get和set
    方法时,如果遇见了ThreadLocal == null的Entry直接将其删除。很多情况下需要使用者手动调用ThreadLocal的remove函数,手动删除不再需要的ThreadLocal来防止内存泄露。因此JDK建议将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露。

先看简介

ThreadLocals rely on per-thread linear-probe hash maps attached to each thread (Thread.threadLocals and inheritableThreadLocals). The ThreadLocal objects act as keys, searched via threadLocalHashCode.

  • ThreadLocal们依赖关联到每个线程上的哈希表。其中以ThreadLocal对象为key,通过threadLocalHashCode来搜索。

从初始化函数切入

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
        if (this instanceof TerminatingThreadLocal) {
            TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
        }
        return value;
    }
  • 我们可以看到使用了ThreadLocalMap这个对象,其为ThreadLocal的内部静态类,阅读后得知其中有Entry类为弱引用(防止垃圾回收无法回收Entry对象)
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
  • 继续阅读初始化函数,通过getMap获取map对象
    ThreadLocal.ThreadLocalMap threadLocals = null;
  • 每一个Thread对象都保存了一个map用来保存ThreadLocal和value副本,如果map为null则在Thread的map中添加一个新的Entry对象(稍后会解释)
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
  • 注意:创建map中新的Entry<key,value>对象的key不是Thread实例的hashcode而是ThreadLocalHashCode
  • 这样每个Thread通过维持一个map数据结构就拥有其所有的ThreadLocal中的变量副本了

Set方法

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }
  • 依旧从Thread实例中获取map,如果没有该变量则在map中新建一个Entry<ThreadLocalHashCode, value>,如果有的话就修改

get方法

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
  • 如果map不为空则获取LocalThreadMap中的Entry对象,以ThreadLocalHashCode在map中匹配value,如果map为空则重新进行初始化

猜你喜欢

转载自blog.csdn.net/weixin_42072543/article/details/88930877