ThreadLocal原理

首先要带着问题去分析,

  • 新城变量的副本存储在那里?
  • 变量副本是怎么从共享的那个变量赋值出来的?源码中的threadlocal是什么时候初始化的

ThreadLocal是如何实现多个线程之间每一个线程都持有该线程都变量副本?如何共享变量的

ThreadLocal提供了set和get方法用来访问当前线程的线程局部变量

public class ThreadLocal {
    ...
    /**
    * Returns the value in the current thread's copy of this
    * thread-local variable.  If the variable has no value for the
    * current thread, it is first initialized to the value 
    * returned
    * by an invocation of the {@link #initialValue} method.
    *
    * @return the current thread's value of this thread-local
    */
    public T get() {
       //获取当前线程
       Thread t = Thread.currentThread();
       //拿到线程中的存储变量副本
       ThreadLocalMap map = getMap(t);
       if (map != null) {
           ThreadLocalMap.Entry e = map.getEntry(this);
           if (e != null)
               return (T)e.value;
       }
       //如果当前线程中的ThreadLocalMap == null就进行初始化的操作
       return setInitialValue();
    }

    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);
       return value;
    }
    
    protected T initialValue() {
        return null;
    }

    ThreadLocalMap getMap(Thread t) {
       return t.threadLocals;
    }
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    ...
}
  • 首先看ThreadLocal类的成员函数get方法的注解,返回当前线程的变量副本,如果当前线程不存在变量副本,那么就初始化一个容量为0的变量副本
  • 分析ThreadLocal类的成员函数get可知,首先根据当前线程去获取它的成员变量threadLocals,他的类型为ThreadLocalMap是ThreadLocal的一个内部类。接下来赋值给局部变量map,紧接着执行map != null,如果是首次执行,此时map==null,那么接下来就会调用ThreadLocal类的成员函数setInitialValue去进行初始化的操作,跟踪代码最终执行到了成员函数createMap,也就是创建了一个ThreadLocalMap的实例并赋值给了当前线程t的成员变量threadLocals;如果不是第一次的操作,那么此时map != null是成立的,那么此时就可以根据ThreadLocal实例直接获取到存储到变量副本

ThreadLocalMap解析

public class ThreadLocal {
    ...
    static class ThreadLocalMap {
        
        static class Entry extends WeakReference {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }
        ...
        private static final int INITIAL_CAPACITY = 16;
        ...
        ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & 
                (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
    }
    ...
}
  • 可以看到ThreadLocalMapThreadLocal的一个内部类,然后还有一个内部类Entry,ThreadLocalMap构造初始化时候,创建了一个size = 16的Entry数据,并且把当前线程持有的ThreadLocal对象作为key传入,当前线程的便利那个副本作为value传入,并存入数据中去,这样子就可以关联起来ThreadLocal与当前线程的变量副本
  • 总结下来其实每个线程的变量副本是存放在自己的成员变量threadLocals中,他的类型是ThrealLocalMap,然后通过ThreadLocal去访问对应的变量副本

变量副本是什么时候创建出来的呢?

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
           map.set(this, value);
        else
           createMap(t, value);
    }
  • 当前线程中的threadLocalMap是null的时候,会调用createMap创建一个新的map,并且赋值给当前的线程的成员变量threadLocals

总结如下:
1、在代码中声明的ThreadLocal对象,实际上只有一个。
2、在每个线程中,都维护了一个threadlocals对象,在没有ThreadLocal变量的时候是null的。一旦在ThreadLocal的createMap函数中初始化之后,这个threadlocals就初始化了。以后每次那个ThreadLocal对象想要访问变量的时候,比如set函数和get函数,都是先通过getMap(t)函数,先将线程的map取出,然后再从这个在线程(Thread)中维护的map中取出数据【以当前threadlocal作为参数】。

你可能感兴趣的:(ThreadLocal原理)