介绍
ThreadLocal提供线程局部变量。跟普通变量不同,每个访问它的线程都有线程自己的,独立初始化的变量副本。ThreadLocal实例通常是作为类的私有静态字段,把类的状态(例如用户名,用户ID或者交易ID)与线程进行关联。
只要线程是存活的并且ThreadLocal实例是可访问的,则每个线程都对其线程局部变量的副本持有隐式引用。线程消失后,其线程本地实例的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用)。
实现原理
在Thread类里面有一个threadLocals的变量,类型是ThreadLocal.ThreadLocalMap,当前线程所有ThreadLocal变量都保存在该map中,key是用户定义的ThreadLocal变量本身,值是ThreadLocal包含的数值。
threadLocals变量在线程中的默认值是null,线程退出时会把threadLocals变量赋值为null。
ThreadLocal操作
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocal内存泄漏
- 使用线程池,线程一直在存活,有多个ThreadLocal实例,有些ThreadLocal变量已经释放掉,有些没有,释放掉的ThreadLocal变量对应的threadLocals指向的ThreadLocalMap的key因为是弱引用而被回收,对应的value值一直没有被回收导致。需要调用remove方法。
- 使用线程池,线程一直在存活,有一个ThreadLocal实例,不同系统的多个用户,每个用户设置自己的用户名,如果一个用户没有设置,可能会导致该用户使用到其他用户的用户名。需要调用remove。
延伸
ThreadLocal.ThreadLocalMap inheritableThreadLocals变量
InheritableThreadLocal继承自ThreadLocal,其提供了一个特性,就是让子线程可以访问在父线程中设置的本地变量。
实现原理是在线程初始化的init里面判断父线程的inheritableThreadLocals不为null,则用该Map创建一个新的Map副本。