ThreadLocal使用方式为在一个线程中创建一个ThreadLocal对象,使用threadLocal.set()赋值,在相同线程的另一个地方使用threadLocal.get()获取值,接下来,从源码角度分析一下ThreadLocal的实现方式以及存在的问题。
1:创建是直接new 一个对象创建出来,既然ThreadLocal与线程已经绑定(一个ThreadLocal在不同的线程之中可以存放不同的值),接下来看下ThreadLocal与线程是如何进行绑定的
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
以上是ThreadLocal的set方法,先获取到当前的线程,然后通过getMap(t)获取到当前线程的ThreadLocalMap,ThreadLocalMap是ThreadLocal的静态内部类,在getMap()方法中可以看到ThreadLocalMap其实是Thread的一个属性(每一个线程都有自己专属的ThreadLocalMap)。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
ThreadLocalMap其实就是一个Map类型的,其中key是ThreadLocal,value是Entity,Entity类的格式如下:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocalMap的其余地方可以不看,主要看下Entry!!!,此类继承了WeakRegerence(虚引用!!),其中此类的value就是我们要保存的值。
因此我们使用ThreadLocal.set方法保存一个值得时候,最终其实就是保存在每个线程专属的Map中,其中key为我们创建的ThreadLocal对象,这下可以明白为什么在多线程之中可以ThreadLocal可以保证线程安全,追踪到底其实就是将在不同线程之中set的值保存到了各自对应线程之中的Map之中,其中key就是ThreadLocal对象本身,也就解释了为什么一个ThreadLocal只能设置一个值,不允许设置多个值。
以上就是ThreadLocal 赋值的流程,如果把这个流程理解清楚,即使不看源码,想必大概get的方法也能猜到了,无非就是获取当前线程的ThreadLocalMap方法,然后通过此map的get(key)获取到值,其中key是当前的ThreadLocal对象。
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();
}
以上是get()的源代码,与我们预想的差不多,只不过加了双重保险,获取的map是空的话会先初始化值进去再返回初始化之后的值,获取值的方法变为了ThreadLocalMap自定义的getEntry方法,获取Entry之后其属性value就是我们在ThreadLocal的set()方法中设置的值。
以上,ThreadLocal源码分析的差不多了,之前强调过的,为什么Entry要继承WeakReference,有什么作用,在此之前,需要了解强引用,软引用,弱引用相关知识点,不多赘述,网上找的一篇比较好的文章https://www.jianshu.com/p/825cca41d962,
关于为什么使用弱引用,通过上面文章可知,弱引用的对象会在下一次垃圾收集器执行的时候被回收。如果我们不是使用弱引用,当在程序里我们把自己定义的threadLocal== null之后,下一次垃圾回收的时候,在Entry的key中还持有者对象的引用,因此我们创建的ThreadLocal不会被GC。