ThreadLocal的作用以及其存在的问题

发布时间 2023-08-18 20:06:40作者: hwj7
ThreadLocal是JDK自带的一个类,他的作用是为每个线程中创建专属本地变量,这些变量只能被当前线程访问和修改,其他线程无法访问,当你创建了一个ThreadLocal里的变量后,每个访问这个变量的线程都会获得一个此变量的本地副本,同时THreadLocal提供了get()、set()方法来获取默认值,或将其值更改为当前线程所存的副本的值。
 
在ThreadLocal的源码中可以发现,其set()方法实际上是通过Thread.currentThread()方法获取当前的线程,再通过getMap获得当前线程的ThreadLocalMap对象,对该对象进行if/else判断,判断这个map是否为空,如果不为空,就往ThreadLocalMap中加入一个键值对,键是ThreadLocal对象,值则是set方法中传入的想要在该线程中存储来进行共享的变量副本,如果为空,那就创建一个新的ThreadLocal对象,并将其存储到该Map中。
 
get()方法与set()方法前面一样,都是获得当前线程的ThreadLocalMap对象,再对map是否为空其进行判断,如果不为空:1、获取其当前ThreadLocal的键值对对象;2、对获取到的键值对对象进行非空判断,不为空则将键值对的值返回出去,为空则不做处理;如果为空,那么不进入if中,调用setInitialValue()方法来获取初始值,并将其设置为当前线程的变量副本。
 
    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();
    }

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

ThreadLocal很有用,但是也存在一些问题,1、内存泄露问题,由于ThreadLocalMap中使用的key是弱引用,而value是强引用,所以如果ThreadLocal没有被外部强引用的时候,如果发生垃圾回收,key会被清理掉,而value不会被清理掉,一旦发生,ThreadlocalMap中就会出现一个key为null的键值对,如果不做处理,value就永远无法被回收,就会产生内存泄漏;2、如果线程数量非常庞大,ThreadlocalMap的查询效率可能会降低,可以考虑使用线程池来控制线程的创建和销毁,来减少ThreadlocalMap的大小,但是如果使用线程池复用线程时没有清除ThreadLocal变量,可能会导致线程之间共享ThreadLocal变量;为了解决以上问题,ThreadlocalMap也提供了一个remove()方法,会清理掉key为null的记录,建议在通过try/catch/finally中的finally代码块来调用remove(),确保其一定会执行。

 

     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null) {
             m.remove(this);
         }
     }