ThreadLocal中用于生存线程的独有变量的数据布局是一个内部类:ThreadLocalMap,也是k-v布局。
key就是当前的ThreadLocal对象,而v就是我们想要生存的值。
Thread类对象中维护了ThreadLocalMap成员变量,而ThreadLocalMap维护了以ThreadLocal为key,必要存储的数据为value的Entry数组。这是它们三者之间的基本包含关系,我们必要进一步到源码中探求踪迹。
检察Thread类,内部维护了两个变量,threadLocals和inheritableThreadLocals,它们的默认值是null,它们的类型是ThreadLocal.ThreadLocalMap,也就是ThreadLocal类的一个静态内部类ThreadLocalMap。
在静态内部类ThreadLocalMap维护一个数据布局类型为Entry的数组,节点类型如下代码所示:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
从源码中我们可以看到,Entry布局现实上是继承了一个ThreadLocal类型的弱引用并将其作为key,value为Object类型。这里利用弱引用是否会产生题目,我们这里临时不讨论,在文章结束的时候一起讨论一下,临时可以理解key就是ThreadLocal对象。对于ThreadLocalMap,我们一起来了解一下其内部的变量:
// 默认的数组初始化容量
private static final int INITIAL_CAPACITY = 16;
// Entry数组,巨细必须为2的幂
private Entry[] table;
// 数组内部元素个数
private int size = 0;
// 数组扩容阈值,默以为0,创建了ThreadLocalMap对象后会被重新设置
private int threshold;
这几个变量和HashMap中的变量十分类似,功能也类似。
ThreadLocalMap的构造方法如下所示:
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
// 初始化Entry数组,巨细 16
table = new Entry[INITIAL_CAPACITY];
// 用第一个键的哈希值对初始巨细取模得到索引,和HashMap的位运算代替取模原理一样
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// 将Entry对象存入数组指定位置
table = new Entry(firstKey, firstValue);
size = 1;
// 初始化扩容阈值,第一次设置为10
setThreshold(INITIAL_CAPACITY);
}
从构造方法的表明中可以了解到,该构造方法是懒加载的,只有当我们创建一个Entry对象并必要放入到Entry数组的时候才会去初始化Entry数组。