众所周知, ThreadLocal 类是 java 提供线程本地变量的工具类. 但父线程的本地变量却不能被子线程使用, 代码如下:
- public static void main(String[] args) {
- ThreadLocal<String> threadLocal = new ThreadLocal<>();
- threadLocal.set("abc");
- System.out.println("父线程:"+threadLocal.get());
- Thread t1 = new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.print("子线程:"+threadLocal.get());
- }
- });
- t1.start();
- }
运行结果如下:
至于原因呢, 得先了解 ThreadLocal 存储的变量是怎么存储的. 首先, 让我们先看看 Thread 类的源码: 在 thread 类中有声明这么一个成员变量 --threadLocals
ThreadLocal.ThreadLocalMap threadLocals = null;
根据定义可以看出, 这是 ThreadLocal 类里的静态内部类, 它的结构是 Map 结构, 以键值对的形式存储值. key 值就是当前 ThreadLocal 类的实例, value 值就是当前线程的本地变量. 所以线程的本地变量是存在线程实例当中的, 而不是存在 ThreadLocal 中. ThreadLocal 只是一个工具类, 体现在当 ThreadLocal 实例调用 set() 方法时, 会将当前线程的 threadLocals 变量实例化. 以下是 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);
- }
可以看到, 先是获取当前线程的实例, 再获取线程的成员变量 threadLocals, 如果 threadLocals 已经实例化, 就直接以当前 ThreadLocal 类为 key, 存储的本地变量为 value, 存进 threadLocals 中. 所以线程本地变量是存储在线程中的. 如果 threadLocals 未实例化, 则调用 createMap() 方法, 该方法会调用 ThreadLocalMap 的构造方法, 初始化一个以当前 ThreadLocal 类为 key, 存储的本地变量为 value 的 Map. 该 Map 是类似 HashMap 的, 感兴趣的话可以继续往下看源码, 此处不做扩展.
回到最开始的问题, 子线程调用 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();
- }
此时, 会产生两个分支, 一个是通过子线程的 ThreadLocalMap 的 getEntry() 方法获取 Entry 并返回 value 值, 但由于子线程的 ThreadLocalMap!= 父线程的 ThreadLocalMap, 所以获取不到父线程的本地变量. 另一个分支是返回 setInitialValue() 的返回值, setInitialValue() 方法源码如下:
- private T setInitialValue() {
- T value = initialValue();
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- return value;
- }
可以看到 value 会默认为 null, 如果子线程的 ThreadLocalMap 已经实例化, 则直接以 ThreadLocal 实例为 key,null 为 value 存进 ThreadLocalMap 中, 否则就创建一个同上所述的 ThreadLocalMap.
引发出的问题: ThreadLocal 类是弱引用, 一次 GC 后会为 null, 当 key 为 null 时, value 值却还存在内存中, 造成内存泄漏. 所以 ThreadLocal 类最后一定要执行 remove() 方法. 在 GC 之前将内存释放.
综上, 第一次写博客, 写得不好或者有错误的地方, 请指正.
来源: https://www.cnblogs.com/doubaozzz/p/12173758.html