ThreadLocal使用

先来分析下类的聚合关系,每个Thread类中有一个ThreadLocalMap类,ThreadLocalMap维护着一个<ThreadLocal<?>, Object>类型的数组,你每次调用ThreadLocal的set()方法时,是将数据存入Thread的ThreadLocalMap中,每次调用ThreadLocal的get()方法,是从Thread的ThreadLocalMap中取数据,可以看看ThreadLocal的set()方法实现:

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

看到了吧,首先得到当前Thread的ThreadLocalMap,然后以ThreadLocal<T> 为key存入进去,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();
}

这里不用考虑线程安全的问题,因为get()和set()是函数,且没有访问全局资源,这样是将临界资源直接存到所有调用线程的Thread类中,这种方式可以解决一部分情况下的多线程同步问题。

接下来我们来研究下ThreadLocal中弱引用的问题,Entry是ThreadLocalMap中存放调用ThreadLocal的set()方法时设置值的结构,我们看到Entry继承了WeakReference<?>,这也是我们使用WeakReference的常用方式,这里的Entry类中key是弱引用类型,当调用ThreadLocal的set(T value)方法时,不仅传递了value,还传递了ThreadLoal实例进去作为弱引用,需要强调的是一个ThreadLocal实例只能往一个线程中保存一个对象,想要保存多个的话,需要定义多个ThreadLocal实例,所以这里的一个Entry对应着某个线程里的一个ThreadLocal实例,这里的Entry中对ThreadLocal的引用是弱引用,但是Thread的ThreadLocalMap中对Entry是强引用,且Thread对ThreadLocalMap也是强引用,外层ThreadLocal实例也通常是持有强引用,所以,当外层的ThreadLocal被回收后,Entry#get()==null就会为true,Entry就知道可以将map中的这对值删除回收了,回收的时机是调用remove()、get()、set()时,所以,这个弱引用就是这个作用了,可以得知外层ThreadLocal的引用状况而回收map中键值对。

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

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

接下来我们说说ThreadLocal使用时可能触发的内存泄露问题,为什么会这样呢?主要原因还是线程生命周期过长,没有正确清理set(T value)方法设置进去的value值,如果线程是运行完毕即结束的就不会有内存泄漏的问题,ThreadLocal导致的内存泄漏多是在线程池中出现的,所以使用时注意一点,,有条件调用remove()方法的一定调用。

猜你喜欢

转载自blog.csdn.net/tales522/article/details/81154826
今日推荐