众所周知,ThreadLocal类是java提供线程本地变量的工具类。但父线程的本地变量却不能被子线程使用,代码以下: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 }
运行结果以下:ide
至于缘由呢,得先了解ThreadLocal存储的变量是怎么存储的。首先,让咱们先看看Thread类的源码:在thread类中有声明这么一个成员变量——threadLocals 工具
ThreadLocal.ThreadLocalMap threadLocals = null;
根据定义能够看出,这是ThreadLocal类里的静态内部类,它的结构是Map结构,以键值对的形式存储值。key值就是当前ThreadLocal类的实例,value值就是当前线程的本地变量。因此线程的本地变量是存在线程实例当中的,而不是存在ThreadLocal中。ThreadLocal只是一个工具类,体如今当ThreadLocal实例调用set()方法时,会将当前线程的threadLocals变量实例化。如下是ThreadLocal类的set()方法源码。this
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的,感兴趣的话能够继续往下看源码,此处不作扩展。spa
回到最开始的问题,子线程调用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()方法源码以下:code
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。blog
引起出的问题:ThreadLocal类是弱引用,一次GC后会为null,当key为null时,value值却还存在内存中,形成内存泄漏。因此ThreadLocal类最后必定要执行remove()方法。在GC以前将内存释放。内存
综上,第一次写博客,写得很差或者有错误的地方,请指正。rem