ThreadLocal
ThreadLocal 是 java提供的一种线程内部数据存储方案,通过ThreadLocal实例提供的get()/set(T)方法可以操作Thead类中的TheadLocalMap实例.从而在Thread内部存储线程私有的数据.
Thread类内部定义了两个 ThreadLocal.ThreadLocalMap类变量,来保存线程数据
public
class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
TheadLocalMap是ThreadLocal中定义的一个静态类,结构基本和HashMap类似,不同的是ThreadLocalMap处理hash冲突的方式用的是线性再探的方式
private Entry[] table;
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
TheadLocalMap有几个需要注意的点:
-
和HashMap不同,TheadLocalMap和是使用线性再探测方法来解决hash冲突,冲突后往后寻找空余的位置存储.
-
这里entry的key是当前的threadLocal实例,value是我们需要存储的私有化数据
-
注意,entry对threadLoca对象的引用是弱引用,这是因为当一个ThreadLocal没有被其他地方引用后,虚拟机可以回收这个ThreadLocal实例的内存,如果这里不是弱引用的话,当一个ThreadLocal实例没有被其他地方引用后,因为TheadLocalMap还持有着这个TheadLocal实例的强引用,所以这个实例一直不会被回收,会导致内存泄露.
为什么Entry对ThreadLocal对象的引用需要时弱引用? ThreadLocal tl = new ThreadLocal(); tl.set(obj); 因为TheadLocalMap中key存的是ThreadLocal对象,所以在上面的操作完成后,有两个地方对tl对象进行了引用,一个是tl对象本身,它对这个堆中的tl对象进行了强引用,另一个是TheadLocalMap中的Entry对象弱引用了tl对象. 在我们需要清理这个线程属性obj的时候,对tl对象赋NULL值 tl = null; 这时tl释放了对tl对象的强引用,按理我们期望GC能够把堆中tl对象给清理掉,但是这时Entry对象还引用着tl对象,如果这个引用也是强引用的话,只要线程不结束,这个tl对象就会一直不被释放,最终可能会导致oom. 这时还有一个问题是,Entry还持有着obj对象的引用,这个引用是强引用(因为其他引用就可能会把obj给gc掉,所以这里必须是强引用),在把tl =null 后,只要线程不结束,obj对象也一直不会被gc,所以在使用ThreadLocal时,一定要用完后remove掉. tl.remove(); ThreadLocal中的set/get/resize方法,在执行的过程中,也会判断是不是有key为null的数据,有的话会删除掉,这样可以一定程度降低内存泄露的概率,但是最好还是用完把当前的threadLocal给remove掉.彻底避免内存泄漏 线程池中的线程也尽量不要用ThreadLocal, 一是有可能内存泄露,二是由于线程复用可能会导致读到脏数据 ps: 强引用: 对象不能被gc 软引用: 空间不足时会被gc 弱引用: 不影响gc 虚引用: 也不影响gc, 主要用于跟踪gc过程.
ThreadLocal常用的方法get()/set()/remove()源码
public T get() {
Thread t = Thread.currentThread();
//InheritableThreadLocal重写了个getMap,所以对于InheritableThreadLocal来说,这里拿到的就是inheritableThreadLocals
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();
}
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 void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
//调用ThreadLocalMap中的remove()方法
m.remove(this);
}
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
//1. 获取map中key对应的Entry
for (Entry e = tab[i];
//只要 e!=null,说明没找到对应的Entry,就一直往后循环,这是因为用的是线性再探的方法处理冲突
e != null;
//e = 下一个Entry
e = tab[i = nextIndex(i, len)]) {
//找到key对应的Entry e
if (e.get() == key) {
//把e = null ,把e在堆中的Entry对象释放掉,这样堆里的key,和value都可以被回收了
e.clear();
//重新hash i后的元素,并且这个过程还会检查key为null的Entry对象并置为null,避免内存泄露
expungeStaleEntry(i);
return;
}
}
}
InheritableThreadLocal
- InheritableThreadLocal继承自ThreadLocal,重写了getMap()/createMap()/childValue()方法,重写的目的是为了能够操作Thread中的inheritableThreadLocals属性,这里存放了父线程中的TheadLocalMap数据.
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
/**
* Computes the child's initial value for this inheritable thread-local
* variable as a function of the parent's value at the time the child
* thread is created. This method is called from within the parent
* thread before the child is started.
* <p>
* This method merely returns its input argument, and should be overridden
* if a different behavior is desired.
*
* @param parentValue the parent thread's value
* @return the child thread's initial value
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* Get the map associated with a ThreadLocal.
*
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
* Create the map associated with a ThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the table.
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
- 在Thread中的初始化方法init()中,会将父线程中的TheadLocalMap拷贝到inheritableThreadLocals中.
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
......
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
......
- 使用InheritableThreadLocal便能操作Thread中的inheritableThreadLocals属性,获取父类的ThreadLocal属性