实际上,主线程的threadLocals属性中的哈希表中一般不止我们上面定义的三个ThreadLocal,因为加载主线程的时候还有可能在其他地方使用到ThreadLocal,笔者某次Debug的结果如下:
用PPT画图简化一下:
上图threadLocalHashCode属性一行的表是为了标出每个Entry的哈希槽的哈希值,实际上,threadLocalHashCode是ThreadLocal@XXXX中的一个属性,这是很显然的,本来threadLocalHashCode就是ThreadLocal的一个成员变量。
上面只是简单粗略对ThreadLocalMap的源码进行了流水账的分析,下文会作一些详细的图,说明一下ThreadLocal和ThreadLocalMap中的一些核心操作的过程。
ThreadLocal的创建从ThreadLocal的构造函数来看,ThreadLocal实例的构造并不会做任何操作,只是为了得到一个ThreadLocal的泛型实例,后续可以把它作为ThreadLocalMap$Entry的键:
// 注意threadLocalHashCode在每个新`ThreadLocal`实例的构造同时已经确定了 private final int threadLocalHashCode = nextHashCode(); private static AtomicInteger nextHashCode = new AtomicInteger(); private static final int HASH_INCREMENT = 0x61c88647; private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); } // 通过Supplier去覆盖initialValue方法 public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) { return new SuppliedThreadLocal<>(supplier); } // 默认公有构造函数 public ThreadLocal() { }注意threadLocalHashCode在每个新ThreadLocal实例的构造同时已经确定了,这个值也是Entry哈希表的哈希槽绑定的哈希值。
TreadLocal的set方法ThreadLocal中set()方法的源码如下:
public void set(T value) { //设置值前总是获取当前线程实例 Thread t = Thread.currentThread(); //从当前线程实例中获取threadLocals属性 ThreadLocalMap map = getMap(t); if (map != null) //threadLocals属性不为null则覆盖key为当前的ThreadLocal实例,值为value map.set(this, value); else //threadLocals属性为null,则创建ThreadLocalMap,第一个项的Key为当前的ThreadLocal实例,值为value createMap(t, value); } // 这里看到获取ThreadLocalMap实例时候总是从线程实例的成员变量获取 ThreadLocalMap getMap(Thread t) { return t.threadLocals; } // 创建ThreadLocalMap实例的时候,会把新实例赋值到线程实例的threadLocals成员 void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }上面的过程源码很简单,设置值的时候总是先获取当前线程实例并且操作它的变量threadLocals。步骤是:
获取当前运行线程的实例。
通过线程实例获取线程实例成员threadLocals(ThreadLocalMap),如果为null,则创建一个新的ThreadLocalMap实例赋值到threadLocals。
通过threadLocals设置值value,如果原来的哈希槽已经存在值,则进行覆盖。
ThreadLocal中get()方法的源码如下:
public T get() { //获取当前线程的实例 Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { //根据当前的ThreadLocal实例获取ThreadLocalMap中的Entry,使用的是ThreadLocalMap的getEntry方法 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T) e.value; return result; } } //线程实例中的threadLocals为null,则调用initialValue方法,并且创建ThreadLocalMap赋值到threadLocals return setInitialValue(); } private T setInitialValue() { // 调用initialValue方法获取值 T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); // ThreadLocalMap如果未初始化则进行一次创建,已初始化则直接设置值 if (map != null) map.set(this, value); else createMap(t, value); return value; } protected T initialValue() { return null; }initialValue()方法默认返回null,如果ThreadLocal实例没有使用过set()方法直接使用get()方法,那么ThreadLocalMap中的此ThreadLocal为Key的项会把值设置为initialValue()方法的返回值。如果想改变这个逻辑可以对initialValue()方法进行覆盖。