共计 2429 个字符,预计需要花费 7 分钟才能阅读完成。
初看 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