ThreadLocal -- 02 -- InheritableThreadLocal源码解析

原文链接:ThreadLocal – 02 – InheritableThreadLocal源码解析


相关文章:


InheritableThreadLocal 继承自 ThreadLocal,用于提供从父线程到子线程值的继承


一、方法解析

1、childValue(T parentValue) 相关方法
  • childValue(T parentValue)

    protected T childValue(T parentValue) {
        return parentValue;
    }
    
    • 在创建子线程时,根据父线程的值来计算可被继承的局部变量在子线程中的初始值

    • 在启动子线程之前,会在父线程中调用此方法,此方法仅返回其输入参数,如果需要其他行为,则需要重写此方法


2、getMap(Thread t) 相关方法
  • getMap(Thread t)

    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
    
    • 获取与 InheritableThreadLocal 相关联的 ThreadLocalMap

3、createMap(Thread t, T firstValue) 相关方法
  • createMap(Thread t, T firstValue)

    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
    
    • 创建与 InheritableThreadLocal 相关联的 ThreadLocalMap

二、流程分析

  • 通过第一步我们会发现,InheritableThreadLocal 仅仅是继承了 ThreadLocal,并重写了 ThreadLocal 中的三个方法而已,那么 InheritableThreadLocal 到底有什么用呢?让我们一起从线程的创建开始说起

    • 1、创建线程

      Thread thread = new Thread();
      
    • 2、Thread 构造方法

      public Thread() {
          init(null, null, "Thread-" + nextThreadNum(), 0);
      }
      
      private void init(ThreadGroup g, Runnable target, String name,
                        long stackSize) {
          init(g, target, name, stackSize, null, true);
      }
      
      private void init(ThreadGroup g, Runnable target, String name,
                        long stackSize, AccessControlContext acc,
                        boolean inheritThreadLocals) {
          // ......
          if (inheritThreadLocals && parent.inheritableThreadLocals != null)
              this.inheritableThreadLocals =
                  ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
          // ......
      }
      
      • 如上所示,Thread 在创建时会调用最基层的构造方法

      • 该构造方法中,有个流程判断 inheritThreadLocals && parent.inheritableThreadLocals != null,如果结果为 true,会将父线程 inheritableThreadLocals 中属性的值继承到自己的 inheritableThreadLocals

        • inheritThreadLocals

          • 该参数为 true 时,表示从子线程会从父线程中继承相关变量
        • parent.inheritableThreadLocals != null

          • 只有父线程的 inheritableThreadLocals 不为 null,子线程才可以从父线程 inheritableThreadLocals 中继承相关属性值
    • 3、创建 ThreadLocalMap

      • 当满足第 2 步中的流程判断时,会调用 ThreadLocal 的 createInheritedMap(ThreadLocalMap parentMap) 方法来创建 ThreadLocalMap

        static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
            return new ThreadLocalMap(parentMap);
        }
        
      • createInheritedMap(ThreadLocalMap parentMap) 方法中又调用了 ThreadLocalMap 的构造方法 ThreadLocalMap(ThreadLocalMap parentMap) 来创建 ThreadLocalMap

        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 对象
                Entry e = parentTable[j];
                // 如果 Entry 对象不为 null
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    /*
                     * 获取弱引用关联的 ThreadLocal 对象
                     * (我们可以简单地将其视为 ThreadLocalMap 的 key,实际上是其弱引用)
                     */
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    // 如果 key 不为 null
                    if (key != null) {
                        /*
                         * 调用 InheritableThreadLocal 的
                         * childValue(T parentValue) 方法来获取 value
                         */
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        // 计算 key 对应的哈希桶数组下标
                        int h = key.threadLocalHashCode & (len - 1);
                        // 如果哈希桶数组对应下标处已经有元素存在,则计算下一个下标
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        // 将 Entry 对象存放至哈希桶数组新计算出来的下标处
                        table[h] = c;
                        size++;
                    }
                }
            }
        }
        
      • 在 ThreadLocalMap 的构造方法 ThreadLocalMap(ThreadLocalMap parentMap) 中,由于只有创建 InheritableThreadLocal 时,才会调用此方法,因此此处的 key 为 InheritableThreadLocal 对象,调用的是 InheritableThreadLocal 中重写的 childValue(T parentValue) 方法

        protected T childValue(T parentValue) {
            return parentValue;
        }
        
  • 通过上面三个步骤,我们可以知道,每个线程在创建时,都会在内部创建一个 ThreadLocal.ThreadLocalMap 类型的变量 inheritableThreadLocals,子线程会从父线程的 inheritableThreadLocals 中继承相关变量

  • 那么再让我们来看看存储和读取的流程

    • 1、创建 InheritableThreadLocal

      ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>()
      
    • 2、调用 set(T value) 方法往 threadLocal 中添加值

      public void set(T value) {
          Thread t = Thread.currentThread();
          ThreadLocalMap map = getMap(t);
          if (map != null)
              map.set(this, value);
          else
              createMap(t, value);
      }
      
      • 通过 set(T value) 方法的源码,我们发现会先调用 getMap(Thread t) 方法,然后判断该方法返回的 ThreadLocalMap 是否不为 null,如果不为 null,则直接覆盖值;如果为 null ,则再调用 createMap(Thread t, T firstValue) 方法来创建一个 ThreadLocalMap,并将其赋值给当前线程的 inheritableThreadLocals 属性

      • 由于我们创建的是一个 InheritableThreadLocal 对象,而 InheritableThreadLocal 正好重写了 getMap(Thread t)createMap(Thread t, T firstValue) 这两个方法,也就是说,调用 set(T value) 方法时,内部调用的都是 InheritableThreadLocal 中重写的方法

        ThreadLocalMap getMap(Thread t) {
           return t.inheritableThreadLocals;
        }
        
        void createMap(Thread t, T firstValue) {
            t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
        }
        
    • 3、调用 get() 方法从 threadLocal 中获取值

      public T get() {
          Thread t = Thread.currentThread();
          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();
      }
      
      • 通过 get() 方法的源码,我们会发现会先调用 getMap(Thread t) 方法 (即获取的是当前线程 inheritableThreadLocals 属性中的值),然后判断该方法返回的 ThreadLocalMap 是否不为 null,如果不为 null,则以当前的 ThreadLocal 作为 key,获取该 ThreadLocalMap 中存储的 Entry;然后再判断该 Entry 是否不为 null,如果不为 null,则获取 Entry 中存储的 value 并返回

      • 如果上述条件都不满足,则调用 setInitialValue() 方法来获取 value 并返回

        private T setInitialValue() {
            T value = initialValue();
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
            return value;
        }
        
        protected T initialValue() {
            return null;
        }
        
      • 由于我们创建的是一个 InheritableThreadLocal 对象,而 InheritableThreadLocal 正好重写了 getMap(Thread t)createMap(Thread t, T firstValue) 这两个方法,也就是说,调用 get() 方法时,内部调用的也都是 InheritableThreadLocal 中重写的方法


三、举例说明

  • 例子一

    public class ThreadLocalTest {
    
        private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
    
        public static void main(String[] args){
            threadLocal.set(123);
            System.out.println("parent thread 【" + threadLocal.get() + "】");
            new Thread(() -> System.out.println("child thread 【" + threadLocal.get() + "】")).start();
            threadLocal.remove();
            
            // parent thread 【123】
            // child thread 【null】
        }
    }
    
    • 如上所示,我们使用 ThreadLocal 来进行存储和读取,首先我们调用 set(T value) 方法往当前线程 (主线程) 的 threadLocals 中添加值 123,接着我们调用 get() 方法从主线程的 threadLocals 中取值,由于此时仍然为主线程,所以取值成功

    • 然后我们 new 了一个新的线程,由它去调用 get() 方法从自己的 threadLocals 中取值,不过此时当前线程已经由主线程变成了 new 出来的新线程,而新线程的 threadLocals 中并没有值,所以取值失败

  • 例子二

    public class ThreadLocalTest {
    
        private static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();
    
        public static void main(String[] args){
            threadLocal.set(123);
            System.out.println("parent thread 【" + threadLocal.get() + "】");
            new Thread(() -> System.out.println("child thread 【" + threadLocal.get() + "】")).start();
            threadLocal.remove();
            
            // parent thread 【123】
            // child thread 【123】
        }
    }
    
    • 如上所示,我们使用 InheritableThreadLocal 来进行存储和读取,首先我们调用 set(T value) 方法往当前线程 (主线程) 的 threadLocals 中添加值 123,接着我们调用 get() 方法从主线程的 threadLocals 中取值,由于此时仍然为主线程,所以取值成功

    • 然后我们 new 了一个新的线程,由它去调用 get() 方法从自己的 threadLocals 中取值,不过此时当前线程已经由主线程变成了 new 出来的新线程,与例子一不同的是,由于我们使用的是 InheritableThreadLocal,因此新线程的 threadLocals 会去继承父线程 (主线程) threadLocals 中的相关变量,所以取值成功


四、参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值