ThreadLocal解析:父线程的本地变量不能传递到子线程详解
coder丶豆包 人气:0众所周知,ThreadLocal类是java提供线程本地变量的工具类。但父线程的本地变量却不能被子线程使用,代码如下:
1 public static void main(String[] args) { 2 ThreadLocal<String> threadLocal = new ThreadLocal<>(); 3 threadLocal.set("abc"); 4 System.out.println("父线程:"+threadLocal.get()); 5 Thread t1 = new Thread(new Runnable() { 6 @Override 7 public void run() { 8 System.out.print("子线程:"+threadLocal.get()); 9 } 10 }); 11 t1.start(); 12 }
运行结果如下:
至于原因呢,得先了解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之前将内存释放。
综上,第一次写博客,写得不好或者有错误的地方,请指正。
加载全部内容