ThreadLocal的分析
ThreadLocal的适用场景
- ThreadLocal适用于保存每个线程都独享的对象
- ThreadLocal适用于保存每个线程独享的对象可供其他方法获取的场景
例如web应用程序中的用户信息可以保存中用线程中,从而在其他需要用到的地方直接可以获取到
ThreadLocal是不是用来解决共享资源多线程访问的问题?
- 不是,ThreadLocal的底层机制决定了每个线程都独立拥有一份空间来存放数据。虽然在效果上线程安全的,但这并不是通过解决共享资源的方式实现的。
底层原理
每一个Thread内部都有一个ThreadLocalMap对象,在ThreadLocalMap中有保存的是ThreadLocal作为key,值作为value的k-v结构
ThreadLocal.get()
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();
}
this就是ThreadLocal,因为ThreadLocalMap中的key就是ThreadLocal,因此通过getEntry(this)的方式取到对应的值
Thread.getMap()
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//InheritableThreadLocal
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
getMap()就是获取Thread的成员变量threadLocals、inheritableThreadLocals
- ThreadLocalMap
ThreadLocalMap
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry是一个内部类,类似于map结构。Entry这里不是采用HashMap的拉链法而是采用的是线性探测法
线性探测法原理如下:
ThreadLocal.remove
ThreadLocal.remove方法。在线程不使用ThreadLocal之后必须进行remove操作,因为在ThreadLocalMap中k-v是强引用关系,会造成内存泄漏。因此在不使用ThreaLocal之后一定要进行remove操作
InheritableThreadLocal
InheritableThreadLocal是子线程能访问到父线程的ThreadLoca
Thread.init()
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
在线程初始化方法中,会判断父线程中的inheritableThreadLocals是否有值,如果有值会将值赋给子线程
总结
由于ThreadLocalMap中的Key是Thread,并且ThreadLocalMap与Key之间是弱引用的关系,Key与value之间是强引用的关系,导致Thread对象被回收后,还存在Key-Value之间的强引用关系,无法回收Value对象,造成内存泄漏;