概述
ThreadLocal是一个java提供的本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不通的变量值完成操作的场景
方法 | 作用 |
public T get() | 获取当前线程的副本变量值 |
public void set(T value) | 保存当前线程的副本变量值 |
public void remove() | 移除当前线程的副本变量值 |
protected T initialValue() | 为当前线程初始副本变量值 |
ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象
底层结构
ThreadLocal是一个工具类,用来管理每个线程中的ThreadLocalMap
ThreadLocalMap就是「存储线程中产生的ThreadLocal变量以及当前线程的变量副本值的一个map表」,ThreadLocalMap是ThreadLocal中的静态内部类,并没有实现map接口,用独立的方式实现了map的功能,其内部的entry也独立实现
- 每个Thread线程内部都有一个Map,即ThreadlLocalMap
- Map里面存储线程本地对象(key)和线程的变量副本(value)
- 但是,Thread内部的Map-ThreadLocalMap是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值。所以对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,互不干扰
源码解析
get()
public T get() {
Thread t = Thread.currentThread();//获得当前线程
ThreadLocalMap map = getMap(t);//ThreadLocalMap是ThreadLocal的一个静态类,
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//传入的是this,this代表当前对象,即当前这个threadLocal
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();//类的私有函数,初始化数据,创建map表
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;//threadLocals是Thread类中的成员变量
}
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);//创建map表并设置数据
return value;
}
protected T initialValue() {
return null;
}
流程:
-
- 获取当前线程t
- 根据t调用getMap()函数获取当前线程的成员变量ThreadLocalMap
- 判断map是否为空
-
-
- 不为空从map中获取线程存储的k-v entry结点,再从entry结点中获取存储的value副本并返回
- 为空调用setInitialValue(),创建map表并设置数据(即数据为null)
-
set()
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);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
流程
-
- 获取当前线程t
- 根据t调用getMap()函数获取当前线程的成员变量ThreadLocalMap
-
-
- map非空,则将threadLocal和新的value副本放入map中
- map空,则对线程的成员变量ThreadLocalMap进行初始化创建,并将threadLocal和value副本放入map中
-
remove()
public void remove() {
//获取线程中的成员变量threadLocalMap
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
流程
也是根据当前线程获取 成员变量thredLocalMap,再移除当前对象
内存泄漏
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);//k是弱引用
value = v;
}
}
Entry中的key是弱引用的,每个key都弱引用执行threadLocal,当threadLocal实例置为null以后,没有任何强引用指向threadLocal实例,所以threadLocal将会被GC回收,但是我们的value却不能回收,而这块value也不会访问到了,只有等到线程结束时value才能会回收,但是如果使用的是线程池,线程干完任务之后就会被放入池中,线程永远不会结束,就会造成内存泄漏。
在ThreadLocal中的set(),get(),remove()方法在每次操作ThreadLocalMap中的值是都会清理ThreadLocalMap中key为null的value
所以,当某个ThreadLocal变量不再使用时,记得调用remove()清理该变量。