ThreadLocal
第一次见到这个单词并非在书中,而是一篇关于用AOP编写动态切换多数据源的文章,里面讲到用ThreadLocl为每一个数据源创建自己的线程来维护,所以概念并不陌生。
https://www.jianshu.com/p/3bb70ae81828
是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。
// ThreadLocal的创建private static ThreadLocal<Long> threadLocal = new ThreadLocal<Long>(){@Overrideprotected Long initialValue() {return System.currentTimeMillis();}};// 记录开始时间public static void begin() {threadLocal.set(System.currentTimeMillis());}// 记录耗时public static Long end() {return System.currentTimeMillis() - threadLocal.get();}
InheritableThreadLocal
这种ThreadLocal可以从父线程传到子线程,也就是子线程能访问父线程中的InheritableThreadLocal
demo
package base;public class ThreadLocalTest {static class ChildThread extends Thread {@Overridepublic void run() {System.out.println(threadLocal.get()); // nullSystem.out.println(inheritableThreadLocal.get());}}public static ThreadLocal<Object> threadLocal = new ThreadLocal<>();public static InheritableThreadLocal<Object> inheritableThreadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {threadLocal.set(" threadLocal");inheritableThreadLocal.set("inheritableThreadLocal");new ChildThread().start();}}
实现原理
很容易想到,因为这个东西是跟着线程走的,所以应该是线程的一个属性,事实上也是这样,ThreadLocal和InheritableThreadLocal都是存储在Thread里面的。
/* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;/** InheritableThreadLocal values pertaining to this thread. This map is* maintained by the InheritableThreadLocal class.*/ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
上边这个就是Thread的两个成员变量,其实两个是一样的类型。
ThreadLocalMap是ThreadLocal的内部类,他里边是一个用一个Entry数组来存数据的。set时讲ThreadLocal作为key,要存的值传进去,他会对key做一个hash,构建Entry,放到Entry数组里边。
// 伪码static class ThreadLocalMap {// 内部的Entry结构static class Entry {...}// 存数据的private Entry[] table;// setprivate void set(ThreadLocal<?> key, Object value) {int i = key.threadLocalHashCode & (len-1);tab[i] = new Entry(key, value);}// getprivate Entry getEntry(ThreadLocal<?> key) {int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];if (e != null && e.get() == key)return e;elsereturn getEntryAfterMiss(key, i, e);}}
再来看看ThreadLocal的get方法
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t); // 这个就是拿到的存在Thread的threadLocals这个变量if (map != null) {// 这里就是毫无难度的事情了ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}// 这个也很简单,他会调你重写的initialValue方法,拿到一个值,set进去并且返回给你// 这个也很有趣,一般init在初始化完成,但是他是在你取的时候去调return setInitialValue();}
再来看看ThreadLocal的set, 超级简单,不多说
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}
ThreadLocal看完了,再来瞅瞅InheritableThreadLocals,看看他是怎么可以从父线程那里拿东西的
// 继承了ThreadLocal, 重写了三个方法public class InheritableThreadLocal<T> extends ThreadLocal<T> {// 这个方法在ThreadLocal是直接抛出一个异常UnsupportedOperationExceptionprotected T childValue(T parentValue) {return parentValue;}// 超简单,我们的Map不要threadLocals了,改为inheritableThreadLocalsThreadLocalMap getMap(Thread t) {return t.inheritableThreadLocals;}// 同上void createMap(Thread t, T firstValue) {t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);}}
发现他和ThreadLocal长得差不多,就是重写了三个方法,由此看来关键在inheritableThreadLocals是如何传递的
直接在Thread里面搜inheritableThreadLocals
你会发现他是在init方法中赋值的,而init实在Thread的构造方法中调用的
// 这个parent就是 创建这个线程的那个线程,也就是父线程if (inheritThreadLocals && parent.inheritableThreadLocals != null)this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
看来现在得看看ThreadLocal.createInheritedMap这个方法了
// parentMap就是父线程的inheritableThreadLocalsstatic ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap);}// 发现很简单,就是把父线程的东西到自己线程的inheritableThreadLocals里边private ThreadLocalMap(ThreadLocalMap parentMap) {Entry[] parentTable = parentMap.table;int len = parentTable.length;setThreshold(len);table = new Entry[len];for (int j = 0; j < len; j++) {Entry e = parentTable[j];if (e != null) {@SuppressWarnings("unchecked")ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();if (key != null) {Object value = key.childValue(e.value);Entry c = new Entry(key, value);int h = key.threadLocalHashCode & (len - 1);while (table[h] != null)h = nextIndex(h, len);table[h] = c;size++;}}}}
总结一下
ThreadLocal和InheritableThreadLocal是基于在Thread里边的两个变量实现的,这两个变量类似于一个HashMap的结构ThreadLocalMap,里边的Entry key为ThreadLocal, value为你存的值. InheritableThreadLocal的实现主要是在线程创建的时候,如果父线程有inheritableThreadLocal, 会被拷贝到子线程。
