ThreadLocal就是所谓的线程本地变量,意思是只能在当前线程获取到的变量,与当前线程绑定在一块的一个变量。
那是怎么做到的呢,看下面的ThreadLocal.set(T value)方法:

  1. public void set(T value) {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null)
  5. map.set(this, value);
  6. else
  7. createMap(t, value);
  8. }
  9. ThreadLocalMap getMap(Thread t) {
  10. return t.threadLocals;
  11. }
  12. ThreadLocal.ThreadLocalMap threadLocals = null;

从上面代码可知,往ThreadLocal里set值时,会先获取当前线程,然后从当前线程对象里获取存放ThreadLocal数据的ThreadLocalMap,这个类型是定义在ThreadLocal内部的一个内部类,这个下面再讲。且从下面的代码可知,ThreadLocalMap是用ThreadLocal对象作为key的一个map:

  1. private void set(ThreadLocal<?> key, Object value) {
  2. ...
  3. ...
  4. ...
  5. }

这也就意味着,每new一个ThreadLocal对象,都只能存放一个值。

由于数据是存放在当前线程对象里面的一个map里,所以是和当前线程绑定的。

ThreadLocal的应用

由于ThreadLocal的特性,我们可以利用这个类来做一些巧妙的应用,比如:

  • 存放数据库连接,这样一个线程内多次get的就是同一个数据库连接。
  • 存放traceId,做调用追踪,日志打印等。

    内存泄漏问题

    image.png
    上面是ThreadLocal的引用关系图。
    ThreadLocalMap中的Entry中的key对ThreadLocal对象的引用是一个弱引用,如果ThreadLocal实例的引用也被置为了null,那么ThreadLocal对象就会被回收,但是Entry里的value却不会,只有当当前线程对象被回收的时候,这个value才会被回收,这样在这段时间内,就会造成内存泄漏。

如果线程仅仅是一次性使用,那么不会有什么大问题,但是在线程池中,线程会被复用,那么就可能造成大量的内存泄漏,最终导致内存溢出。所以如果是在线程池中使用ThreadLocal,使用完了之后,一定记得remove。