美文网首页
不就是ThreadLocal

不就是ThreadLocal

作者: xian_cheng | 来源:发表于2019-03-01 19:03 被阅读0次

    最近在重构代码时,发现有不同类之间参数的传递很复杂,想到了之前看到的ThreadLocal,于是就想使用ThreadLocal来解决参数传递的问题,但是在使用之前还是先看了下ThreadLocal的源码,避免后面出现问题。

    先简单说下ThreadLocal的实现原理,然后再跟着源码看下。

    每个ThreadLocal实例对应一个当前运行的Thread线程,每个Thread线程又有一个ThreadLocalMap,通过ThreadLocal类的set方法将需要使用的参数保存在ThreadLocalMap这个map中,后面只要在同一个线程中利用ThreadLocal实例的get方法就可以得到之前设置的参数。
    其实,在看源码之前,可以简单思考下实现的套路无非就是:
    1. 获取当前线程。
    2. 获取此线程的ThreadLocalMap
    3. 根据这个map的key获取value。

    那么,问题来了,这个key是啥呢,那就开始看下源码吧。
    首先看下ThreaLocal的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);
        }
    
        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    

    其实,从源码来看,实现的套路和我们之前猜想的是一样的,主要是看下这个map的key是啥,从map.set(this, value)这句可以看到,这个map的key就是ThreadLocal实例的引用。

    然后,看下ThreaLocal的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方法的过程也很简单,根据threadLocal实例的引用去获取ThreadLocalMap中Entry对象,然后获取value值。

    上面的源码很简单,主要的问题是ThreadLocal可能存在内存泄露,这也是使用之前想看下源码的原因,下面就看下为啥会出现内存泄露。
    内存泄露一定是存储的数据没有及时释放,导致数据占用的内存越来越大,从上面的源码来看,数据是存储在哪里的呢,很明显是在ThreadLocalMap中,那放在map中的数据为啥占用的内存越来越大呢,那就要看下这个map的源码有啥特点了。

        static class ThreadLocalMap {
        
            static class Entry extends WeakReference<ThreadLocal<?>> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
    

    从上面的源码可以发现,Entry对象继承了WeakReference类,这个类的作用主要就是使某个对象的引用为弱引用,那么弱引用有啥特点呢,简单来说,只要触发GC,不管这个实例有没有被其他对象引用,都会被回收。
    写到这里,又想到前段时间利用WeakHashMap来简单实现缓存功能,其实也是利用了WeakHashMap弱引用的特点,避免缓存越来越大,导致内存溢出。
    继续刚才的分析,可能不太熟悉的同学这里可能会有疑问,既然弱引用这么容易回收,那么更不可能出现内存泄露了。其实,只是map的key为弱引用,那么key回收后就变为了null,但是value还没有被回收呢。假如,我们使用的是线程池,由于线程池中的线程不会被释放,那么这个线程中对应的ThreadLocalMap也就一直不会被回收,如果线程很多的话,那么ThreadLocalMap占用的内存就越来越大,这样的话就可能会出现内存溢出问题。

    那么,如何解决呢,其实很简单,每次使用完ThreadLocal中保存的参数后,调用ThreadLocal的remove方法删除即可。

    相关文章

      网友评论

          本文标题:不就是ThreadLocal

          本文链接:https://www.haomeiwen.com/subject/bqljuqtx.html