ThreadLocal是一个线程外部的存储类,能够在指定线程内存储数据,数据存储当前,只有指定线程能够失去存储数据,ThreadLocal提供了线程内存储变量的能力,这些变量不同之处在于每一个线程读取的变量是对应的相互独立的。通过get和set办法就能够失去以后线程对应的值,ThreadLocal不是用来解决共享对象的多线程拜访问题的,通过ThreadLocal的set()办法设置到线程的ThreadLocal.ThreadLocalMap里的是是线程本人要存储的对象,其余线程不须要去拜访,也是拜访不到的。各个线程中的ThreadLocal.ThreadLocalMap以及ThreadLocal.ThreadLocal中的值都是不同的对象。

1、每个线程都有一个本人的ThreadLocal.ThreadLocalMap对象

2、每一个ThreadLocal对象都有一个循环计数器

3、ThreadLocal.get()取值,就是依据以后的线程,获取线程中本人的ThreadLocal.ThreadLocalMap,而后在这个Map中依据第二点中循环计数器获得一个特定value值

set

1、先对ThreadLocal外面的threadLocalHashCode取模获取到一个table中的地位

2、这个地位上如果有数据,获取这个地位上的ThreadLocal

(1)判断一下地位上的ThreadLocal和我自身这个ThreadLocal是不是一个ThreadLocal,是的话数据就笼罩,返回

(2)不是同一个ThreadLocal,再判断一下地位上的ThreadLocal是是不是空的,这个解释一下。Entry是ThreadLocal弱援用,"static class Entry extends WeakReference<ThreadLocal>",有可能这个ThreadLocal被垃圾回收了,这时候把新设置的value替换到以后地位上,返回

(3)下面都没有返回,给模加1,看看模加1后的table地位上是不是空的,是空的再加1,判断地位上是不是空的...始终到找到一个table上的地位不是空的为止,往这外面塞一个value。换句话说,当table的地位上有数据的时候,ThreadLocal采取的是方法是找最近的一个空的地位设置数据。

3、如果没有数据,实例化一个新的ThreadLocalMap,并赋值给线程的成员变量threadLocals

    /**     * Sets the current thread's copy of this thread-local variable            将此线程局部变量的以后线程正本设置为指定值。 大多数子类将不须要重写此办法,而仅依附{@link #initialValue}办法来设置线程局部变量的值。      * to the specified value.  Most subclasses will have no need to     * override this method, relying solely on the {@link #initialValue}     * method to set the values of thread-locals.     *     * @param value the value to be stored in the current thread's copy of         @param value要存储在此本地线程的以后线程正本中的值。     *        this thread-local.     */    public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }        /**         * Create the map associated with a ThreadLocal. Overridden in         * InheritableThreadLocal.         *         * @param t the current thread         * @param firstValue value for the initial entry of the map         */        void createMap(Thread t, T firstValue) {            t.threadLocals = new ThreadLocalMap(this, firstValue);        }        /**         * Set the value associated with key.         *         * @param key the thread local object         * @param value the value to be set         */        private void set(ThreadLocal<?> key, Object value) {            // We don't use a fast path as with get() because it is at            // least as common to use set() to create new entries as            // it is to replace existing ones, in which case, a fast            // path would fail more often than not.            Entry[] tab = table;            int len = tab.length;            //对ThreadLocal外面的threadLocalHashCode取模获取到一个table中的地位            int i = key.threadLocalHashCode & (len-1);           //这个地位上如果有数据,获取这个地位上的ThreadLocal            for (Entry e = tab[i];                 e != null;                 e = tab[i = nextIndex(i, len)]) {                ThreadLocal<?> k = e.get();               //判断一下地位上的ThreadLocal和我自身这个ThreadLocal是不是一个ThreadLocal,是的话数据就笼罩,返回                if (k == key) {                    e.value = value;                    return;                }               //不是同一个ThreadLocal,再判断一下地位上的ThreadLocal是是不是空的,Entry是ThreadLocal弱援用,"static class Entry extends WeakReference<ThreadLocal>",有可能这个ThreadLocal被垃圾回收了                if (k == null) {                    //设置新的value替换到以后地位上,返回                    replaceStaleEntry(key, value, i);                    return;                }            }           //下面都没有返回,给模加1,看看模加1后的table地位上是不是空的,是空的再加1,判断地位上是不是空的...始终到找到一个table上的地位不是空的为止,往这外面塞一个value。换句话说,当table的地位上有数据的时候,ThreadLocal采取的               //是方法是找最近的一个空的地位设置数据            tab[i] = new Entry(key, value);            int sz = ++size;            if (!cleanSomeSlots(i, sz) && sz >= threshold)                rehash();        }

get

1、获取以后线程

2、尝试去以后线程中拿它的ThreadLocal.ThreadLocalMap

3、以后线程中判断是否有ThreadLocal.ThreadLocalMap

(1)有就尝试依据以后ThreadLocal的threadLocalHashCode取模去table中取值,有就返回,没有就给模加1持续找,这和设置的算法是一样的

(2)没有就调用set办法给以后线程ThreadLocal.ThreadLocalMap设置一个初始值

/** * Returns the value in the current thread's copy of this        返回此线程局部变量的以后线程正本中的值。 如果该变量没有以后线程的值,则首先将其初始化为调用{@link #initialValue}办法返回的值。 * 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();    //尝试去以后线程中拿它的ThreadLocal.ThreadLocalMap    ThreadLocalMap map = getMap(t);    //判断以后线程中判断是否有ThreadLocal.ThreadLocalMap    if (map != null) {        //有就尝试依据以后ThreadLocal的threadLocalHashCode取模去table中取值,有就返回,没有就给模加1持续找        ThreadLocalMap.Entry e = map.getEntry(this);        if (e != null) {            @SuppressWarnings("unchecked")            T result = (T)e.value;            return result;        }    }    //没有就调用set办法给以后线程ThreadLocal.ThreadLocalMap设置一个初始值    return setInitialValue();}        /**         * Get the entry associated with key.  This method                     * itself handles only the fast path: a direct hit of existing         * key. It otherwise relays to getEntryAfterMiss.  This is         * designed to maximize performance for direct hits, in part         * by making this method readily inlinable.         *         * @param  key the thread local object         * @return the entry associated with key, or null if no such         */        private Entry getEntry(ThreadLocal<?> key) {            //依据以后ThreadLocal的threadLocalHashCode取模去table中取值            int i = key.threadLocalHashCode & (table.length - 1);            Entry e = table[i];            //有就返回,没有就给模加1持续找            if (e != null && e.get() == key)                return e;            else                return getEntryAfterMiss(key, i, e);        }

remove

    /**     * Removes the current thread's value for this thread-local        删除此线程局部变量的以后线程值。 如果此线程局部变量随后被以后线程{@linkplain #get read}调用,则其值将通过调用其{@link #initialValue}办法来从新初始化,除非     * variable.  If this thread-local variable is subsequently        以后值是{@linkplain #set set} 在此期间穿线。 这可能会导致在以后线程中屡次调用{@code initialValue}办法。     * {@linkplain #get read} by the current thread, its value will be     * reinitialized by invoking its {@link #initialValue} method,     * unless its value is {@linkplain #set set} by the current thread     * in the interim.  This may result in multiple invocations of the     * {@code initialValue} method in the current thread.     *     * @since 1.5     */     public void remove() {         //获得以后线程的ThreadLocal.ThreadLocalMap,如果有ThreadLocal.ThreadLocalMap,找到对应的Entry,移除掉         ThreadLocalMap m = getMap(Thread.currentThread());         if (m != null)             m.remove(this);     }

ThreadLocalMap

实例化ThreadLocalMap时创立了一个长度为16的Entry数组。通过hashCode与length位运算确定出一个索引值i,这个i就是被存储在table数组中的地位,每个线程Thread都持有一个Entry型的数组table,而所有的读取过程都是通过操作这个数组table实现的。

/** * Construct a new map initially containing (firstKey, firstValue).    结构一个最后蕴含(firstKey,firstValue)的Map。 ThreadLocalMaps是提早结构的,因而只有在至多有一个条目要搁置时才创立一个。 * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {    //长度为16的Entry数组    table = new Entry[INITIAL_CAPACITY];    //被存储在table数组中的地位    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);    table[i] = new Entry(firstKey, firstValue);    size = 1;    //设置长度    setThreshold(INITIAL_CAPACITY);} /**   * Set the resize threshold to maintain at worst a 2/3 load factor.    从新设置长度   */private void setThreshold(int len) {     threshold = len * 2 / 3;}

论断

  • ThreadLocal不须要key,因为线程外面本人的ThreadLocal.ThreadLocalMap不是通过链表法实现的,而是通过开地址法实现的
  • 每次set的时候往线程外面的ThreadLocal.ThreadLocalMap中的table数组某一个地位塞一个值,这个地位由ThreadLocal中的threadLocaltHashCode取模失去,如果地位上有数据了,就往后找一个没有数据的地位
  • 每次get的时候也一样,依据ThreadLocal中的threadLocalHashCode取模,获得线程中的ThreadLocal.ThreadLocalMap中的table的一个地位,看一下有没有数据,没有就往下一个地位找
  • 既然ThreadLocal没有key,那么一个ThreadLocal只能塞一种特定数据。如果想要往线程外面的ThreadLocal.ThreadLocalMap里的table不同地位塞数据 ,比方说想塞三种String、一个Integer、两个Double、一个Date,请定义多个ThreadLocal,ThreadLocal反对泛型"public class ThreadLocal<T>"