1、对象关系图
源码
1、ThreadLocal.set
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //1、去当前线程中获取Map
if (map != null)
map.set(this, value); //2、ThreadLocal对象作为key 值作为value 放到Map中。
else
createMap(t, value);
}
2、ThreadLocal.get
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //1、当前线程中获取Map
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); //2、用ThreadLocal对象作为key取值
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;//3、返回值
return result;
}
}
return setInitialValue();
}
3、ThreadLocal原理总结
- 每个Thread维护着一个ThreadLocalMap的引用
- ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
- 调用ThreadLocal的set()方法时,实际上就是往当前线程的ThreadLocalMap内设置值,key是ThreadLocal对象,值是传递进来的对象
- 调用ThreadLocal的get()方法时,实际上就是往当前线程的ThreadLocalMap获取值,key是ThreadLocal对象
- ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。
正因为这个原理,所以ThreadLocal能够实现“数据隔离”,获取当前线程的局部变量值,不受其他线程影响~
4、QA
内存泄露
虽然ThreadLocalMap中的key是弱引用,当不存在外部强引用的时候,就会自动被回收,但是Entry中的value依然是强引用。这个value的引用链条如下:
可以看到,只有当Thread被回收时,这个value才有被回收的机会,否则,只要线程不退出,value总是会存在一个强引用。但是,要求每个Thread都会退出,是一个极其苛刻的要求,对于线程池来说,大部分线程会一直存在在系统的整个生命周期内,那样的话,就会造成value对象出现泄漏的可能。处理的方法是,在ThreadLocalMap进行set(),get(),remove()的时候,都会进行清理: