初看ThreadLocal代码时候,感觉到很绕。区区三个类,但他们之间的类关系和设计思路与咱们平时写的业务代码是不太一样的。

让咱们一起来看看这三个类之间盘根错节的关系吧。

三者关系概览

从上图咱们能够发现Thread 中持有一个ThreadLocalMap ,这里你能够简略了解为就是持有一个数组,这个数组是Entry 类型的。 Entry 的key 是ThreadLocal 类型的,value 是Object 类型。也就是一个ThreadLocalMap 能够持有多个ThreadLocal。

三者之间的类关系

为什么ThreadLocalMap被设计为ThreadLocal的外部类

为什么要将ThreadLocalMpa设计为ThreadLocal的外部类,而不独立进去呢?其实这里波及到外部类起到封装的作用,咱们一起看一下源码

/\*\* \* ThreadLocalMap is a customized hash map suitable only for \* maintaining thread local values. No operations are exported \* outside of the ThreadLocal class. The class is package private to \* allow declaration of fields in class Thread.  To help deal with \* very large and long-lived usages, the hash table entries use \* WeakReferences for keys. However, since reference queues are not \* used, stale entries are guaranteed to be removed only when \* the table starts running out of space. \*/static class ThreadLocalMap {}

次要是阐明ThreadLocalMap 是一个线程本地的值,它所有的办法都是private 的,也就意味着除了ThreadLocal 这个类,其余类是不能操作ThreadLocalMap 中的任何办法的,这样就能够对其余类是通明的。同时这个类的权限是包级别的,也就意味着只有同一个包上面的类能力援用ThreadLocalMap 这个类,这也是Thread 为什么能够援用ThreadLocalMap 的起因,因为他们在同一个包上面。

ThreadLocal中的ThreadLocalMap是什么时候和Thread绑定在一起

尽管Thread 能够援用ThreadLocalMap,然而不能调用任何ThreadLocalMap 中的办法。这也就是咱们平时都是通过ThreadLocal 来获取值和设置值,看下以下代码

public class Test {

public static void main(String\[\] args) {    ThreadLocal<String> local = new ThreadLocal<>();    local.set("hello world");    System.out.println(local.get());}

}

在第一次调用ThreadLocal set() 办法的时候开始绑定的,来咱们看下set 办法的源码

咱们这里看似是调用ThreadLocal的set办法设置了hello world,实际上这个值曾经被设置到以后线程Thread领有的ThreadLocalMap中去了。 

public void set(T value) {    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null)        map.set(this, value);    else    //第一次的时候进来这里,因为ThreadLocalMap 还没和Thread 绑定        createMap(t, value);}//这个时候开始创立一个新的ThreadLocalMap 赋值给Thread 进行绑定void createMap(Thread t, T firstValue) {    t.threadLocals = new ThreadLocalMap(this, firstValue);}

咱们调用ThreadLocal 的get 办法的时候,其实咱们最初是通过调用ThreadLocalMap 来获取值的。看完了get和set办法后,咱们会发现ThreadLocalMap其实是一个两头媒介,对于使用者而言咱们与咱们间接打交道的是ThreadLocal,只不过咱们要想把值和线程绑定在一起是离不开ThreadLocalMap这个媒介的。

public T get() {    //这里通过获取以后的线程    Thread t = Thread.currentThread();    //通过线程来获取ThreadLocalMap ,还记得咱们下面说的Thread 外面有一个ThreadLocalMap 属性吗?就是这里用上了    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();}ThreadLocalMap getMap(Thread t) {    return t.threadLocals;}

当初大家应该明确了,其实ThreadLdocalMap 对使用者来说是通明的,能够当作空气,咱们一值应用的都是ThreadLocal,这样的设计在应用的时候就显得简略,而后封装性又特地好。

援用文章:

https://blog.csdn.net/weixin_45127309/article/details/102746772