ThreadLocal源码剖析

常用方法

  • get()方法用来获取ThreadLocal在当前线程中保存副本变量。
  • set()用来设置当前线程中副本变量。
  • remove()用来移除当前线程中副本变量。
  • initialValue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法。

我们来看一下ThreadLocal类是如何为每个线程创建一个变量的副本的。

先看下get方法的实现:

第一句是取得当前线程。再看一下getMap方法中做了什么:

在getMap中,是调用当期线程t,返回当前线程t中的一个成员变量threadLocals,继续去Thread类中的成员变量threadLocals看一下是什么:

实际上就是一个ThreadLocalMap,这个类型是ThreadLocal类的一个内部类,继续去看ThreadLocalMap的实现:

可以看到ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值。再继续看setInitialValue方法的具体实现:

很容易了解,就是如果map不为空,就设置键值对,为空,再创建Map,看一下createMap的实现:

总结

  • 实际通过ThreadLocal创建的副本是存储在每个线程Thread内部的一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals中的,键值为当前ThreadLocal变量,value为副本变量(即T类型的变量)。
  • 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。可以通过get方法在threadLocals里面查找使用副本变量。
  • 为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量为其提供多种副本变量。
  • 在进行get之前,必须先set,否则会报空指针异常;如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。因为在上面的代码分析过程中,我们发现如果没有先set的话,即在map中查找不到对应的存储,则会通过调用setInitialValue方法返回i,而在setInitialValue方法中,有一个语句是T value = initialValue(), 而默认情况下,initialValue方法返回的是null。
0%