最近又聊到了ThreadLocal关键字,然后就梳理一下其中内容。这次直接读源码,从源码角度来分析一下。ThreadLocal提供了一个线程私有的区域,线程可以自己放一下值进去,然后用的时候取出来。那么它是怎么实现的呢?
比如这段程序,就基本展示了ThreadLocal怎么用的。从set点进去,看一下源码:
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }可以看到基本流程: (1)首先找到当前的thread (2)调用getMap,得到一个ThreadLocalMap对象 (3)如果map是空,新建一个,否则就set值进去。 这里继续看一下getMap的实现
/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; }getMap其实就是Thread对象的一个成员,说明Thread类里面有一个ThreadLocalMap的成员。点进去Thread类里面看一下: 在第182行确实定义了这个成员,但是这个成员是的类型是:ThreadLocal.ThreadLocalMap,说明其实ThreadLocalMap 是定义在ThreadLocal里面的。 到这里,我们基本搞那个明白了一个事实ThreadLocal 之所以做到了线程私有,其实是当前线程里面有一个ThreadLocalMap的类,数据都是被存在了这个类里面,并且这个类在Thread类里面默认是null,所以就有了开始set的那一段代码然后回来继续看一下ThreadLocal类
在Thread类里面可以看到ThreadLocalMap的定义是:
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;这里直接用ThreadLocal访问了ThreadLocalMap类,点进去看一下: 可以发现,原来ThreadLocalMap类是ThreadLocal的一个静态内部类,这个类里面也有一个静态内部类:Entry,这个就是map的实体了。可以看到源码:
/** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }在加上之前set里面的代码: 可以发现,这个map的key是这个ThreadLocal的WeakReference,value是我们丢进来的值。
在上面的分析中可以看出来,ThreadLocal存放变量的原理,其实就是存放在了Thread自己的ThreadLocalMap里面,并且key是ThreadLocal的一个弱引用。as we know,弱应用在gc的时候是一定会被回收的,如果这个时候value是一个强引用,那么map里面就会出现一个key为null的值,这是不符合实际的。这也就是ThreadLocal的内存泄漏问题。为了解决这个问题,其实ThreadLocal提供了一个remove方法,并且在get,set里面都会调用这个方法,及时清除掉key为null的对象。