ThreadLocal对象的作用就是为每一个线程开辟出一份副本区域,用于存储当前线程的变量信息。
当它在存入对象信息的时候
public void set(T value) {
//获取当前线程变量信息
Thread t = Thread.currentThread();
//根据当前线程获取ThreadLocalMap对象,ThreadLocalMap是一个实现了类似Map集合功能的对象
ThreadLocalMap map = getMap(t);
//如果ThreadLocalMap存在,则把当前LocalThread对象和值存入
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap内部存储的其实是一个Entry对象,key为当前的threadLocal对象,此对象继承了弱引用的类。
static class Entry extends WeakReference> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal> k, Object v) {
//此处会把k包装成一个弱引用对象,也就是ThreadLocalMap的Key为弱引用对象
super(k);
value = v;
}
}
强引用:当对象还被使用的时候,JVM永远不会回收,宁愿抛出OOM;
软引用:当系统内存不足时,JVM的GC会回收这些对象信息,可以定义一些缓存信息,当内存不足,JVM回收,也只是缓存失效,并不一定影响系统功能(除非触发缓存雪崩);
弱引用:不管系统内存是否足够,GC都会回收;
虚引用:虚引用相当于没有引用;
那ThreadLocal存储的key为什么要用弱引用
1、作用就是在key不被使用的时候能被GC回收,防止OOM;
那么我们在使用ThreadLocal的时候,先存入,后面代码在使用,中间已经触发了GC,那么这时候对象是不是已经被GC回收了;
1、当然不是,如果这样就被回收,那这个对象其实毫无用处,因为JVM的GC一般由JVM控制,不是我们代码控制什么时候GC(当然System.gc()主动触发也可以);
2、我们在使用ThreadLocal对象的时候是 ThreadLocal
3、但我们为什么要把key设置成弱引用呢,这样可能引起OOM,假如不设置成弱引用,当我们设置threadLoacl=null时,我们希望它里面的Entry对象也被回收掉,但如果Entry是强引用的话,JVM就不会回收这个Entry对象了,而且这个对象以后都不可达,这样就很容易造成OOM;
那怎么处理key为null的value呢,它在set(),get(),remove()方法的时候,如果发现他的key为null,会对它对应value做处理,所以,如果要处理减少OOM,只需要在使用完成对象之前,根据实际业务调用下上面三个方法中的一个就可以了。
private void set(ThreadLocal> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
当定义过多的ThreadLocal会有什么坏处
1、ThreadLocal对象每一个线程都是一个副本,高并发内存消耗过大(这也是ThreadLocal在高并发场景的一个作用空间换时间,synchronized等加锁问题正好相反,时间还空间);
2、ThreadLocalMap虽然不是map,但它实现了map类似的功能,内部也使用了数组链表的存储方式,当出现了hash碰撞的时候,它会往下继续寻址,直到找到一个空的地址存放数据,这样当链表过长的时候,获取数据的时间也会更多,降低查询性能。