概述

源码介绍:

 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 *

大体上是这个意思:

这个类提供了线程局部变量。这些变量的不同在于每一个线程都有自己的独立初始化的变量副本(通过它的get和set方法)。在希望将状态与线程关联的类中,ThreadLocal实例通常是私有的、静态的(例如,用户ID或事务ID)。


相信你绝对在网上看见过这样的描述:

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路;ThreadLocal的目的是为了解决多线程访问资源时的共享问题。


看完源码介绍,你也应该知道上面这种解释到底是对还是不对了吧~


简单总结ThreadLocal的作用:

提供线程内部的局部变量,在线程生命周期内有效。(再次强调,不是为了线程之间共享数据~)


常用API

  • public void set(T value)    设置当前线程的值
  • public T get()              拿出当前线程的值
  • public void remove()        删除当前线程的值
  • protected T initialValue()初始化当前线程的值(注意这是一个protected修饰的方法。JDK告诉我们可以自己实现)

实现方式

贴出一点源码你就知道是怎样实现的了

  • 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);
 }
  • getMap
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
}
  • threadLocals是什么?  
/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
  • threadLocals在哪里初始化的? 看下set方法的createMap
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
}
  • ThreadLocalMap的源码大家可以去看下
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
}

设计思路总结:

每个Thread维护一个ThreadLocalMap映射表,这个表的key是当前线程本身,value是真正需要存储的对象。


问题 : ThreadLocal会引起内存泄露

  • 为什么?
ThreadLocalMap使用ThreadLocal的弱引用作为key.
贴一张网图

如上图,ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用引用他,那么系统gc的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:

ThreadLocal Ref -> Thread -> ThreaLocalMap -> Entry -> value

永远无法回收,造成内存泄露。


  • 怎么办?
  1. 手动调用ThreadLocal的remove函数,删除不使用的ThreadLocal。
  2. 将ThreadLocal设置为 private static (看下本文开始的译文),这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露。

    参考链接:
https://www.zhihu.com/question/23089780