ThreadLocal 的简介
ThreadLocal 直译过来就是“线程本地的”; 相当于线程自己的小仓库。
ThreadLocal原理
1、简单使用
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("hello world");
String s = threadLocal.get();
System.out.println(s);
threadLocal.remove();
2、方法详解:
set: 放值(向自己的小仓库中放点东西儿)
threadLocal.set("hello world");
源码:
public class ThreadLocal<T> {
public void set(T value) {
//当前线程线程实例对象
Thread t = Thread.currentThread();
//通过当前线程实例获取到ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null)
//如果Map不为null,则以当前threadLocl实例为key,值为value进行存入
map.set(this, value);
else
//map为null,则新建ThreadLocalMap并存入value
createMap(t, value);
}
}
createMap源码:
public class ThreadLocal<T> {
void createMap(Thread t, T firstValue) {
//创建ThreadLocalMap存放值
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//最终创建Entry 存放 值
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
//继承了 弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
}
上述整个流程可总结为:
1、创建ThreadLocal对象;
2、向对象中存入一个值;在存入值得过程中,如果ThreadLocalMap存在,直接向里面存入值,如果不存在需要先创建ThreadLocalMap对象,再创建Entry对象放入值,因此最终值是存放在Entry中!
其中threadLocal实例对象作为Key; val为存入得值,需要注意key是虚引用。
总结为图如下:
实线表示强引用,虚线表示虚引用。
如果把threadlocal的强引用取消,发生GC,此时会带来什么问题 ?
threadLocal = null;
threadLocal = null,即1号线会断开,发生GC,根据可达性,ThreadLocal的内存空间会发生回收,那么此时Entry中的key 就会指向一个null地址,但是此时value还占用一片内存,此时就会发生内存泄漏,key为空,但是value不为空,value的内存空间得不到释放。内存泄漏分析、解决见文末超链接。
get: 从小仓库中取数据。
remove: 删除数据,一般的在使用完成后要记得删除数据,避免内存泄漏。