ThreadLocal

第一次见到这个单词并非在书中,而是一篇关于用AOP编写动态切换多数据源的文章,里面讲到用ThreadLocl为每一个数据源创建自己的线程来维护,所以概念并不陌生。
https://www.jianshu.com/p/3bb70ae81828

是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。

  1. // ThreadLocal的创建
  2. private static ThreadLocal<Long> threadLocal = new ThreadLocal<Long>(){
  3. @Override
  4. protected Long initialValue() {
  5. return System.currentTimeMillis();
  6. }
  7. };
  8. // 记录开始时间
  9. public static void begin() {
  10. threadLocal.set(System.currentTimeMillis());
  11. }
  12. // 记录耗时
  13. public static Long end() {
  14. return System.currentTimeMillis() - threadLocal.get();
  15. }

InheritableThreadLocal

这种ThreadLocal可以从父线程传到子线程,也就是子线程能访问父线程中的InheritableThreadLocal
demo

  1. package base;
  2. public class ThreadLocalTest {
  3. static class ChildThread extends Thread {
  4. @Override
  5. public void run() {
  6. System.out.println(threadLocal.get()); // null
  7. System.out.println(inheritableThreadLocal.get());
  8. }
  9. }
  10. public static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
  11. public static InheritableThreadLocal<Object> inheritableThreadLocal = new InheritableThreadLocal<>();
  12. public static void main(String[] args) {
  13. threadLocal.set(" threadLocal");
  14. inheritableThreadLocal.set("inheritableThreadLocal");
  15. new ChildThread().start();
  16. }
  17. }

实现原理

很容易想到,因为这个东西是跟着线程走的,所以应该是线程的一个属性,事实上也是这样,ThreadLocal和InheritableThreadLocal都是存储在Thread里面的。

  1. /* ThreadLocal values pertaining to this thread. This map is maintained
  2. * by the ThreadLocal class. */
  3. ThreadLocal.ThreadLocalMap threadLocals = null;
  4. /*
  5. * InheritableThreadLocal values pertaining to this thread. This map is
  6. * maintained by the InheritableThreadLocal class.
  7. */
  8. ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

上边这个就是Thread的两个成员变量,其实两个是一样的类型。
ThreadLocalMap是ThreadLocal的内部类,他里边是一个用一个Entry数组来存数据的。set时讲ThreadLocal作为key,要存的值传进去,他会对key做一个hash,构建Entry,放到Entry数组里边。

  1. // 伪码
  2. static class ThreadLocalMap {
  3. // 内部的Entry结构
  4. static class Entry {...}
  5. // 存数据的
  6. private Entry[] table;
  7. // set
  8. private void set(ThreadLocal<?> key, Object value) {
  9. int i = key.threadLocalHashCode & (len-1);
  10. tab[i] = new Entry(key, value);
  11. }
  12. // get
  13. private Entry getEntry(ThreadLocal<?> key) {
  14. int i = key.threadLocalHashCode & (table.length - 1);
  15. Entry e = table[i];
  16. if (e != null && e.get() == key)
  17. return e;
  18. else
  19. return getEntryAfterMiss(key, i, e);
  20. }
  21. }

再来看看ThreadLocal的get方法

  1. public T get() {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t); // 这个就是拿到的存在Thread的threadLocals这个变量
  4. if (map != null) {
  5. // 这里就是毫无难度的事情了
  6. ThreadLocalMap.Entry e = map.getEntry(this);
  7. if (e != null) {
  8. @SuppressWarnings("unchecked")
  9. T result = (T)e.value;
  10. return result;
  11. }
  12. }
  13. // 这个也很简单,他会调你重写的initialValue方法,拿到一个值,set进去并且返回给你
  14. // 这个也很有趣,一般init在初始化完成,但是他是在你取的时候去调
  15. return setInitialValue();
  16. }

再来看看ThreadLocal的set, 超级简单,不多说

  1. public void set(T value) {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null)
  5. map.set(this, value);
  6. else
  7. createMap(t, value);
  8. }

ThreadLocal看完了,再来瞅瞅InheritableThreadLocals,看看他是怎么可以从父线程那里拿东西的

  1. // 继承了ThreadLocal, 重写了三个方法
  2. public class InheritableThreadLocal<T> extends ThreadLocal<T> {
  3. // 这个方法在ThreadLocal是直接抛出一个异常UnsupportedOperationException
  4. protected T childValue(T parentValue) {
  5. return parentValue;
  6. }
  7. // 超简单,我们的Map不要threadLocals了,改为inheritableThreadLocals
  8. ThreadLocalMap getMap(Thread t) {
  9. return t.inheritableThreadLocals;
  10. }
  11. // 同上
  12. void createMap(Thread t, T firstValue) {
  13. t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
  14. }
  15. }

发现他和ThreadLocal长得差不多,就是重写了三个方法,由此看来关键在inheritableThreadLocals是如何传递的
直接在Thread里面搜inheritableThreadLocals
你会发现他是在init方法中赋值的,而init实在Thread的构造方法中调用的

  1. // 这个parent就是 创建这个线程的那个线程,也就是父线程
  2. if (inheritThreadLocals && parent.inheritableThreadLocals != null)
  3. this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

看来现在得看看ThreadLocal.createInheritedMap这个方法了

  1. // parentMap就是父线程的inheritableThreadLocals
  2. static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
  3. return new ThreadLocalMap(parentMap);
  4. }
  5. // 发现很简单,就是把父线程的东西到自己线程的inheritableThreadLocals里边
  6. private ThreadLocalMap(ThreadLocalMap parentMap) {
  7. Entry[] parentTable = parentMap.table;
  8. int len = parentTable.length;
  9. setThreshold(len);
  10. table = new Entry[len];
  11. for (int j = 0; j < len; j++) {
  12. Entry e = parentTable[j];
  13. if (e != null) {
  14. @SuppressWarnings("unchecked")
  15. ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
  16. if (key != null) {
  17. Object value = key.childValue(e.value);
  18. Entry c = new Entry(key, value);
  19. int h = key.threadLocalHashCode & (len - 1);
  20. while (table[h] != null)
  21. h = nextIndex(h, len);
  22. table[h] = c;
  23. size++;
  24. }
  25. }
  26. }
  27. }

总结一下
ThreadLocal和InheritableThreadLocal是基于在Thread里边的两个变量实现的,这两个变量类似于一个HashMap的结构ThreadLocalMap,里边的Entry key为ThreadLocal, value为你存的值. InheritableThreadLocal的实现主要是在线程创建的时候,如果父线程有inheritableThreadLocal, 会被拷贝到子线程。