通常的业务开发中,ThreadLocal 有两种典型的使用场景。
- ThreadLocal 用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全。
- ThreadLocal 用作每个线程内需要独立保存信息,以便供其他方法更方便地获取该信息的场景。每个线程获取到的信息可能都是不一样的,前面执行的方法保存了信息后,后续方法可以通过 ThreadLocal直接获取到,避免了传参,类似于全局变量的概念。
ThreadLocal
会隔离不同线程之间的操作,使得相互不受影响。那么底层的存储结构是怎样的?
首先ThreadLocal
是线程维度的,Thread
,ThreadLocal
,ThreadLocalMap
之间是什么关系?
如图Thread1
,这是一个线程,它的箭头指向了ThreadLocalMap1
,其要表达的意思是,每个Thread 对象中都持有一个ThreadLocalMap
类型的成员变量,在这里 Thread 1
所拥有的成员变量就是 ThreadLocalMap1
。
而ThreadLocalMap
自身类似于是一个Map
,里面会有一个个 key-value 形式的键值对。那么就来看一下它的 key 和 value 分别是什么。可以看到这个表格的左侧是 ThreadLocal1、ThreadLocal2…… ThreadLocaln,能看出这里的 key 就是 ThreadLocal 的引用。
而在表格的右侧是一个一个的 value,这就是我们希望 ThreadLocal 存储的内容,例如起那么所说的全部变量 user 对象等。
这里需要重点看到它们的数量对应关系:一个 Thread
里面只有一个ThreadLocalMap
,而在一个 ThreadLocalMap
里面却可以有很多的 ThreadLocal
,每一个 ThreadLocal 都对应一个 value。因为一个 Thread
是可以调用多个ThreadLocal
的,所以 Thread
内部就采用了 ThreadLocalMap
这样 Map 的数据结构来存放 ThreadLocal
和 value
。
通过这张图片,我们就可以搞清楚 Thread、 ThreadLocal 及 ThreadLocalMap 三者在宏观上的关系了。
源码分析
知道了它们的关系之后,我们再来进行源码分析,来进一步地看到它们内部的实现。
- get 方法
源码
1 | java复制代码public T get() { |
这是 ThreadLocal
的 get 方法,可以看出它利用了 Thread.currentThread
来获取当前线程的引用,并且把这个引用传入到了 getMap
方法里面,来拿到当前线程的 ThreadLocalMap
。
然后就是一个 if ( map != null )
条件语句,那我们先来看看 if (map == null)
的情况,如果 map == null
,则说明之前这个线程中没有创建过 ThreadLocalMap,于是就去调用 setInitialValue
来创建;如果 map != null
,我们就应该通过 this 这个引用(也就是当前的 ThreadLocal
对象的引用)来获取它所对应的 Entry,同时再通过这个 Entry 拿到里面的 value,最终作为结果返回。
值得注意的是,这里的 ThreadLocalMap
是保存在线程 Thread 类中的,而不是保存在ThreadLocal
中的。
- getMap 方法
源码
1 | java复制代码ThreadLocalMap getMap(Thread t) { |
可以看到,这个方法很清楚地表明了 Thread
和 ThreadLocalMap
的关系,可以看出 ThreadLocalMap
是线程的一个成员变量。这个方法的作用就是获取到当前线程内的 ThreadLocalMap
对象,每个线程都有 ThreadLocalMap
对象,而这个对象的名字就叫作 threadLocals
,初始值为 null,代码如下:
1 | java复制代码ThreadLocal.ThreadLocalMap threadLocals = null; |
- set 方法
源码
1 | java复制代码public void set(T value) { |
set 方法的作用是把想要存储的 value 给保存进去。可以看出,首先,它还是需要获取到当前线程的引用,并且利用这个引用来获取到 ThreadLocalMap
;然后,如果 map == null
则去创建这个 map,而当 map != null
的时候就利用 map.set 方法,把 value 给 set 进去。
可以看出,map.set(this, value)
传入的这两个参数中,第一个参数是 this,就是当前 ThreadLocal
的引用,这也再次体现了,在 ThreadLocalMap
中,它的 key 的类型是 ThreadLocal
;而第二个参数就是我们所传入的 value,这样一来就可以把这个键值对保存到 ThreadLocalMap
中去了。
- ThreadLocalMap 类,也就是 Thread.threadLocals
ThreadLocalMap
这个类,下面这段代码截取自定义在ThreadLocal
类中的ThreadLocalMap
类:
1 | java复制代码static class ThreadLocalMap { |
ThreadLocalMap
类是每个线程 Thread
类里面的一个成员变量,其中最重要的就是截取出的这段代码中的 Entry
内部类。在 ThreadLocalMap
中会有一个 Entry 类型的数组,名字叫 table。我们可以把 Entry 理解为一个 map,其键值对为:
- 键,当前的ThreadLocal;
- 值,实际需要存储的变量
总结:主要分析了Thread
、 ThreadLocal
和 ThreadLocalMap
这三个非常重要的类的关系。用图画的方式表明了它们之间的关系:一个 Thread 有一个 ThreadLocalMap
,而 ThreadLocalMap
的 key 就是一个个的 ThreadLocal
,它们就是用这样的关系来存储并维护内容的.
本文转载自: 掘金