今天熟悉公司TMS系统的时候发现自己之前对ThreadLocal的理解是错误的。之前以为ThreadLocal就是一个有自己变量副本的Thread。今天查看文档和源代码的时候才发现根本不是那回事,ThreadLocal根本不是Thread,而是保持Thread变量副本的“容器”,那ThreadLocal是怎么保存这个变量副本的呢?而这个变量副本又怎么能区分是来至那个Thread的呢?
- 查看ThreadLocal的源代码,存储变量副本的是一个定制的Map,ThreadLocal里面的一个内部类。下面以ThreadLocal的set方法说明下
/** * Sets the current thread's copy of this thread-local variable * 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 * 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 * @param map the map to store. */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } /** * ThreadLocalMap is a customized hash map suitable only for * maintaining thread local values. No operations are exported * outside of the ThreadLocal class. The class is package private to * allow declaration of fields in class Thread. To help deal with * very large and long-lived usages, the hash table entries use * WeakReferences for keys. However, since reference queues are not * used, stale entries are guaranteed to be removed only when * the table starts running out of space. */ static class ThreadLocalMap { ...... //Map的一些基础操作,详细请查看ThreadLocal源代码 }
- 每一个ThreadLocalMap都保存在当前Thread里,所以当前Thread 通过ThreadLocal.get()拿出来的就是之前保存的变量副本
public class Thread implements Runnable { /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ...... ThreadLocal.ThreadLocalMap threadLocals = null; ....... }
public class ThreadLocal<T>{ ..... /** * 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; } return setInitialValue(); } /** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } ..... }