ThreadLocal使用
ThreadLocal不是用做数据同步,一般使用在不同的线程中记录不同的数据副本,相当于每个线程拥有自己的某个变量。通常使用方法如下:
ThreadLocal threadLocal =new ThreadLocal<>();
threadLocal.set("wo");
threadLocal.get();
每个线程共享一个threadLocal,但是threadLocal里存的数据却是不同的线程有不同的副本,不同线程之间不能共享。
还可以在创建的时候,设置一个初始值:
static ThreadLocalthreadLocal =new ThreadLocal(){
@Override
protected String initialValue() {
return "a";
}
};
ThreadLocal.set()方法实现
下面看一下threadLocal.set("wo");源码
public void set(T value) {
Thread t = Thread.currentThread(); //获取当前线程
ThreadLocalMap map = getMap(t); //从当前线程中获取线程中ThreadLocalMap对象,实现见下面
if (map !=null)
map.set(this, value);
else
createMap(t, value);
}
getMap(t)方法的实现
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
当本地线程有ThreadLocalMap对象,那么直接把该threadLocal变量和对应的value值放到ThreadLocalMap对象中。
当本地线程中没有创建ThreadLocalMap 对象的时候,执行createMap()方法,给当前线程创建一个ThreadLocalMap对象。
void createMap(Thread t,T firstValue) {
t.threadLocals =new ThreadLocalMap(this, firstValue);
}
创建一个新的ThreadLocalMap对象赋值给当前线程t.threadLocals变量。
ThreadLocalMap的实现包括
Entry:一个内部类,一共是两个部分:key是threadLocal对象的弱引用,Value是传入到threadLocal的值
Entry[] table:Entry数组,这样的实现,可以保证一个线程可以持有多个threadLocal对象及其对应的value值。
ThreadLocal.get()方法实现
public T get() {
Thread t = Thread.currentThread();//获取当前线程
ThreadLocalMap map = getMap(t); //获取当前线程的ThreadLocalMap 对象
if (map !=null) {
//如果当前线程有ThreadLocalMap 对象,
//那么就从该对象中查找传入的threadLocal的Entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e !=null) { //如果entry存在返回value值
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果前面条件判断失败,那么就会返回threadLocal变量设置的默认值
return setInitialValue();
}
ThreadLocal内存泄漏
在使用ThreadLocal的时候,对象内存图如下:
因为在ThreadLocalMap的实现中Entry类的key是弱引用,也就是threadLocal是放在了弱引用队列中(图中虚线),当threadLocal失去了强引用(实线A断掉),那么Entry的key会被回收,但是当前线程如果不结束,value却不会被回收,因此value值会发现泄漏,通常正确的做法是,当使用一个threadlocal的时候,调用threadlocal.remove方法。