Java并发包(ThreadLocal )第一节
一、ThreadLocal能做什么?
- "线程本地变量"或"线程局部变量"
- 作用域为当前线程,而不是某个具体任务。
- 声明周期和线程的声明周期相同(JDK实现中比线程的生命周期更短,减少了内存泄漏的可能)。
- 线程与任务剥离,从而达到线程封闭的目的。
二、存在的问题
线程死亡之后,任务对象可能仍然存在(这才是最普遍的情况),从而ThreadLocal对象仍然存在。我们不能要求线程在死亡之前主动删除其使用的ThreadLocal对象,所以valueMap中该线程对应的entry()无法回收。
这种实现“将线程相关的域封闭于任务对象,而不是线程中”。所以ThreadLocal的实现中最重要的一点就是——“将线程相关的域封闭在当前线程实例中”,虽然域仍然在任务对象中声明、set和get,却与任务对象无关。
三、ThreadLocal源码分析
-
void set(T value)方法
源码:
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * overridethis method, relying solely on the {@link#initialValue} * method to set the values of thread-locals. * * @paramvalue the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { //1. 获取当前线程实例对象 Thread t = Thread.currentThread(); //2. 通过当前线程实例获取到ThreadLocalMap对象 ThreadLocalMap map = getMap(t); if (map != null) //3. 如果Map不为null,则以当前threadLocl实例为key,值为value进行存入 map.set(this, value); else //4.map为null,则新建ThreadLocalMap并存入value createMap(t, value); }
数据value是真正的存放在了ThreadLocalMap这个容器中了,并且是以当前threadLocal实例为key
获取ThreadLocalMap
//****** //ThreadLocal values pertaining to this thread. This map is maintained /* by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; /******* /** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @returnthe map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
设置value时,要根据当前线程t获取一个ThreadLocalMap类型的map,真正的value保存在这个map中。这验证了之前的一部分想法——ThreadLocal变量保存在一个“线程相关”的map中。
也就是说ThreadLocalMap的引用是作为Thread的一个成员变量,被Thread进行维护的。回过头再来看看set方法,当map为Null的时候会通过createMap(t,value)方法:
/** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @paramt the current thread * @paramfirstValue value for the initial entry of the map */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
该方法就是new一个ThreadLocalMap实例对象,然后同样以当前threadLocal实例作为key,值为value存放到threadLocalMap中,然后将当前线程对象的threadLocals赋值为threadLocalMap。
set方法总结:
通过当前线程对象thread获取该thread所维护的threadLocalMap,
若threadLocalMap不为null,则以threadLocal实例为key,值为value的键值对存入threadLocalMap,
若threadLocalMap为null的话,就新建threadLocalMap然后在以threadLocal为键,值为value的键值对存入即可。
- T get()方法
/** * 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. * * @returnthe current thread's value of this thread-local */ public T get() { //1. 获取当前线程的实例对象 Thread t = Thread.currentThread(); //2. 获取当前线程的threadLocalMap ThreadLocalMap map = getMap(t); if (map != null) { //3. 获取map中当前threadLocal实例为key的值的entry ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") //4. 当前entitiy不为null的话,就返回相应的值value T result = (T)e.value; return result; } } //5. 若map为null或者entry为null的话通过该方法初始化,并返回该方法返回的value return setInitialValue(); } /** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @returnthe initial value */ 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; }
这个方法是protected修饰的也就是说继承ThreadLocal的子类可重写该方法,实现赋值为其他的初始值。
关于get方法来总结一下:
通过当前线程thread实例获取到它所维护的threadLocalMap,然后以当前threadLocal实例为key获取该map中的键值对(Entry),若Entry不为null则返回Entry的value。如果获取threadLocalMap为null或者Entry为null的话,就以当前threadLocal为Key,value为null存入map后,并返回null。
- remove()
/** * Removes the current thread's value for this thread-local * variable. If this thread-local variable is subsequently * {@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 * {@codeinitialValue} method in the current thread. * * @since1.5 */ public void remove() { //1. 获取当前线程的threadLocalMap ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) //2. 从map中删除以当前threadLocal实例为key的键值对 m.remove(this); }
删除数据当然是从map中删除数据,先获取与当前线程相关联的threadLocalMap然后从map中删除该threadLocal实例为key的键值对即可。
参考链接:
- 《java高并发程序设计》