ThreadLocal学习心得

  • Post author:
  • Post category:其他


一、ThreadLocal是什么

ThreadLocal为线程本地变量,早在JDK 1.2的版本中就提供java.lang.ThreadLocal,其特点为提供了线程内部存储能力,且变量只在本线程中使用,线程之间不共享。

二、ThreadLocal的使用



思考1




方法A调用方法B,方法B调用方法C,层层调用,直到调用方法Z,如何将变量X从方法A到方法Z中使用呢?

可以采用的方法有:

①变量X一层层传递,略显麻烦;

②将X设置为静态(static),但多线程同时读写共享资源X时,会产生数据不一致问题;

③将X放入ThreadLocal中,同一线程中,任一方法都可取用X;



思考2




观察下列代码,思考控制台能否打印出“hello A”。

public class ThreadLocalTest {

    static ThreadLocal<String> tl = new ThreadLocal<>();

    public static void main(String[] args) {

        //线程A
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(tl.get());
        }).start();

        //线程B
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tl.set("hello A");
        }).start();
    }
}

结论:控制台打印null,线程A无法get到内容。ThreadLocal为线程本地变量,线程之间彼此隔离。

三、ThreadLocal的实现原理

set方法源码:

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */

    public void set(T value) {
       //1.获取当前线程
        Thread t = Thread.currentThread();
       //2.将当前线程对象作为key值,传入 getMap() 方法中,最后返回ThreadLocalMap
        ThreadLocalMap map = getMap(t);
       //3.如果map不为null,则以ThreadLocal对象为key,放入ThreadLocal的对象为value,放入到map中
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

接下来我们看第2步,getMap() 方法具体是什么

    /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

接收到线程后,返回的是线程的成员变量!且类型是ThreadLocalMap。


    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocalMap中是这样描述的:

    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;
            }
        }
       ……
}

可以看到ThreadLocalMap的key(也就是ThreadLocal)是弱引用,会被GC当做垃圾回收掉。回收后ThreadLocalMap的key是null,而value还在,生命周期跟Thread一样长,如果没有手动删除对应key会造成


内存泄露






解决该问题的办法:使用完ThreadLocal后,调用remove方法。

remove方法源码:

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

文章写到这里,基本了解了ThreadLocal的使用,以及为什么能做到存储的变量可以线程之间彼此隔离。本文为自己的学习心得,有不足之处或者需要补充的地方欢迎在评论区交流,大家互相学习,共同进步。



版权声明:本文为ZhangXS9722原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。