ThreadLocal
ThreadLocal线程本地变量:对于同一个static ThreadLocal,不同线程只能供get、set、remove自己的变量,而不会影响其他线程的变量。
Thread.ThreadLocalMap<ThreadLocal, Object>;
1、Thread:当前线程
2、ThreadLocal
3、Object:当前线程共享变量
线程死掉的时候,变量ThreadaLocal就销毁。
ThreadLocalMap<ThreadLocal,Object>键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map<Thread, Object>键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多
当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap<null, Object>的键值对,造成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)
1、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;
2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。
public T get() {
Thread t = Thread.currentThread();
//获得map对象
ThreadLocalMap map = getMap(t);
if (map != null) {
//注意:这里传入的是this对象,而不是线程t
//获取map中的键值对
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocal为每个线程创建变量的副本:
在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。
初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
总结:
1、通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的
2、在进行get之前,必须先set,否则会报空指针异常
3、因为每个线程可以有多个ThreadLocal变量,所以键值为ThreadLocals类型