ThreadLocal源码解析

什么是ThreadLocal? ThreadLocal官方介绍是线程内部存储类,各个线程私有的变量可用于ThreadLocal进行存取,线程之间的变量是不可见的

ThreadLocal源码分析

ThreadLocal.set(T)方法
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
}

从上面代码可能会对ThreadLocal有错误的理解,ThreadLocal内部维护一个map,用当前线程作为key,value作为值进行存储,这是完全错误。实际上ThreadLocal使用并不是一个map,而是线程维护ThreadLocalMap对象进行存储变量的,ThreadLocalMap内部维护一个Entry对象数组,Entry对象存放着弱引用的当前ThreadLocal对象和实际的value值。

初始化ThreadLocalMap内部Entry对象数组

ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
    //初始化Entry对象数组长度为16
    table = new Entry[INITIAL_CAPACITY];
    //当前ThreadLocal对象进行hash运算&(数组长度-1)得到数组下标
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    //创建Entry对象存放在对应数组下标位置
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
   //设置加载因子 数组容量*2/3
    setThreshold(INITIAL_CAPACITY);
}

详细看下如何创建Entry对象

        static class Entry extends WeakReference> {
            Object value;
            Entry(ThreadLocal k, Object v) {
              //调用父类构造方法
                super(k);
                value = v;
            }
        }
-------------------------------super(k);-------------------------------------------------
    public WeakReference(T referent) {
        super(referent);
    }
-------------------------------super(referent);------------------------------------------
    Reference(T referent) {
        this(referent, null);
    }
    //最终当前ThreadLocal实例对象存在再此处referent
    Reference(T referent, ReferenceQueue queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }

从上面代码可以看出ThreadLocal内部维护ThreadLocalMap对象,ThreadLocalMap有Entry[] 属性用于存在线程变量,当前ThreadLocal对象。

获取线程变量get
    public T get() {
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的ThreadLocalMap 如果为空初始化ThreadLocalMap 并赋值为当前值为空
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            //通过当前ThreadLocal对象获取Entry数组下标获取Entry对象
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
-------------------------------map.getEntry(this)-------------------------------------------------
        private Entry getEntry(ThreadLocal key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
清除remove()
        private void remove(ThreadLocal key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
             //遍历整个数组清除所有与当前线程和ThreadLocal对象相关的数据
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                  //判断数组下标对应referet对象和当前ThreadLocal对象是否同一个,若是清除
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

谈一谈ThreadLocal扩容机制:在set到entry[]数组中会进行key过期处理,当清理过后数组长度大于len2/3进行一个rehash操作,在扩容方法之前再次进行一次key清理,清理过后如果数组长度>=len2/3 - len2/31/4=len/2两步判断最终是否需要扩容

InheritableThreadLocal源码分析

InheritableThreadLocal继承了ThreadLocal重写了childValue,getMap,createMap三个方法,InheritableThreadLocal与ThreadLocal区别在于,主线程设置的ThreadLocal在子线程中是无法获取到主线程ThreadLocal内的变量,而InheritableThreadLocal却可以获取的到

    static ThreadLocal threadLocal = new ThreadLocal();
    static InheritableThreadLocal tableLocal = new InheritableThreadLocal();
    public static void main(String[] args) throws InterruptedException {
        threadLocal.set("main Thread current value is ThreadLocal");
        tableLocal.set("main Thread current value is InheritableThreadLocal");
        new Thread(() -> {
            System.out.println("child Thread get parent value :" + threadLocal.get());
        }).start();
        new Thread(() -> {
            System.out.println("child Thread get parent value :" + tableLocal.get());
        }).start();
    }

child Thread get parent value :null
child Thread get parent value :main Thread current value is InheritableThreadLocal

区别在于new Thread线程会调用线程init方法,init方法有个判断如下:

if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

父线程的Thread中inheritableThreadLocals是否为空inheritableThreadLocals也是ThreadLocal.ThreadLocalMap对象,如果不为空进行子线程的InheritableThreadLocal创建

        //parentMap是父线程的ThreadLocalMap
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            //父线程的entry[]数组
            Entry[] parentTable = parentMap.table;
            //父线程的entry[]数组长度
            int len = parentTable.length;
            //设置子线程的加载因子
            setThreshold(len);
            //创建与父线程entry数组一样长度的Entry数组
            table = new Entry[len];
            //遍历将父线程的值copy到子线程的Entry数组中
            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal key = (ThreadLocal) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }
 

                            
                        
                    
                    
                    

你可能感兴趣的:(ThreadLocal源码解析)