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);
    }

在set时,首先获得当前线程对象,然后通过getMap(t)拿到线程的ThreadLocalMap,并将值设入ThreadLocalMap中。

而ThreadLocalMap可以理解为一个Map(虽然不是,但是你可以把它简单理解成HashMap),下面看一下getMap(t)的代码

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

实际上ThreadLocalMap是Thread线程类的局部变量。但是类是ThreadLocal.ThreadLocalMap。

ThreadLocal.ThreadLocalMap threadLocals = null;

下面我们看一下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();
    }

在了解ThreadLocal的内部实现之后,我们自然会引出一个问题。那就是这些变量是维护在Thread类内部的,这也意味着,只要线程不退出,对象的引用将一直存在。

当线程退出时,Thread类会清除ThreadLocalMap

    /**
     * This method is called by the system to give a Thread
     * a chance to clean up before it actually exits.
     */
    private void exit() {
        if (group != null) {
            group.threadTerminated(this);
            group = null;
        }
        /* Aggressively null out all reference fields: see bug 4006245 */
        target = null;
        /* Speed the release of some of these resources */
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }

但是,如果我们使用的是线程池,线程可能不会真的退出,如果将一些大对象保存在ThreadLocal中,那这些对象占用的内存可能永远会得不到释放。这就是ThreadLocal的内存泄露问题 。

如果希望及时回收不再使用的对象,可以通过ThreadLocal.remove()方法移除。