属于Thread中的一个静态内部类
public class Thread implements Runnable {{
ThreadLocal.ThreadLocalMap threadLocals = null;
}
public class ThreadLocal<T>{
//这里是真正存储数据的地方
ThreadLocal.ThreadLocalMap threadLocals = null;
//这是一个静态内部类
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
}
}
}
- ThreadLocal存储的key是new ThreadLocal对象本身,所以一个ThreadLocal对象只有一个Value,多次set会覆盖。
- 存储的位置在Thread的成员变量中,如下所示:
ThreadLocal.ThreadLocalMap threadLocals = null;
一个Thread只有一个ThreadLocalMap,ThreadLocalMap中有一个entery数组,如下所示:
private Entry[] table;
Entry的所有代码如下,我们的Value就存储在Entry的value中。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
因为一个Thread只有一个ThreadLocalMap,而我们却可以在一个Thread线程中创建多个ThreadLocal对象?如下所示:
public class Test{
private ThreadLocal td1 = new ThreadLocal();
private ThreadLocal td2 = new ThreadLocal();
private ThreadLocal td3 = new ThreadLocal();
// 我们可以创建多个ThreadLocal
}
当多个ThreadLocal对象set值时,我们会根据ThreadLocal对象的hashcode计算出其所在的Entry数组的下标位置,计算方式如下(是不是跟HashMap计算数组下标类似)
int i = key.threadLocalHashCode & (len-1);
(相当于取余数)
总结:所以使用Entry数组存储ThreadLocal的value的是为了一个Thread中创建多个ThreadLoca对象。
1.观察如下代码,当method()调用完成后程序继续执行,此时ThreadLocal的对象引用会被回收掉,如果Entry是强引用的情况下就会发送内存泄漏。
public void test(){
method();
//程序继续执行
//more code ,继续处理业务逻辑
}
public void method(){
ThreadLocal td = new ThreadLocal();
td.set("1GB");
}
原因分析:
当继续处理业务逻辑时,method方法已经执行完成了,ThreadLocal的引用(td)已经断开,按理应该回收堆中的ThreadLocal实例,可是却被key(红色)的所引用,无法回收到,由于主线程对象一直还在运行,所以ThreadLocalMap实例一直无法回收,导致Entry数组中的key和value也就无法回收调,此时就发送了内存泄漏。
解决方案;
如果此时Entry替换成弱引用(黄色虚线),则ThreadLocal只剩下一个弱引用,会立即被回收,此时Entry对象会被回收,那么value自然也会被回收调。
关于弱引用的规范如下:
垃圾回收器一旦发现只具有弱引用的对象,不管当前内存空间是否足够,都会回收它的内存