ThreadLocal内部是个啥玩意?

简介

ThreadLocal和Synchronized一样,都是用于处理线程间变量问题;后者有用于等待方式处理变量,前者用多个副本处理对象,时间和空间牺牲;那么ThreadLocal内部是如何用副本的形式管理的呢?继续往下看

ThreadLocal一般使用

ThreadLocal<String> t = new ThreadLocal<>();
t.set("name");

一般就是上面的用法,我们看看代码内部是如何实现的?

public 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);
}

set设置值时,会getMap()获取一个ThreadLocalMap,如果这个map是一个null的话,就需要创建一个ThreadLocalMap,那么他是如创建的呢?

void createMap(Thread t, T firstValue) {
    
    
 	t.threadLocals = new ThreadLocalMap(this, firstValue);
}

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    
    
 	table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

在createMap中就是new了一个ThreadLocalMap,但是要注意他把这个map赋值给了t,也就是Thread,所以如果是同一个线程里面有多个ThreadLocal对象,其实是用同一个ThreadLocalMap
ThreadLocalMap内部结构,是一个Entry数组,通过ThreadLocal的hashcode确定在数组中的位置,然后key是ThreadLocal,value是设置的值;那这个Entry又是什么结构呢?

static class Entry extends WeakReference<ThreadLocal<?>> {
    
    
/** The value associated with this ThreadLocal. */
     Object value;

     Entry(ThreadLocal<?> k, Object v) {
    
    
         super(k);
         value = v;
     }
 }

从上可以知道,Entry的key实质是一个弱引用,也就是Map持有的ThreadLocal是一个弱引用,map的持有并不影响对它的GC,但是value会影响,map被Thread持有,而Thread几乎可以作为GC Root的根节点(根搜索法),很有可能造成内存泄漏

比如这种情况就会有内存泄漏,当前Thread线程存活时间很久,并且有多个ThreadLocal成员,如果其中一个ThreadLocal不用了,那么这个local的value由于Thread存活很久而造成内存泄漏

官方也意识到了这个问题,修改了部分源码,在对ThreadLocal调用get时候会自动清楚掉Entry数组中key为null的value,这样就不会造成内存泄漏了;

但是还是可能会出现,一种就是粗心的程序员没有去get;另一种是把ThreadLocal设置为static属性

总结

综上所述,同一个线程中的多个ThreadLocal共用一个Entry的Table;不同线程有自己独立的table;为了不造成内存泄漏,使用完后get或者remove掉,以防出现内存泄漏

猜你喜欢

转载自blog.csdn.net/jackzhouyu/article/details/104424465