zoukankan      html  css  js  c++  java
  • InheritableThreadLocal详解

    InheritableThreadLocal详解     https://www.jianshu.com/p/94ba4a918ff5

    InheritableThreadLocal——父线程传递本地变量到子线程的解决方式及分析
    https://blog.csdn.net/hewenbo111/article/details/80487252

    1、简介

    在上一篇 ThreadLocal详解 中,我们详细介绍了ThreadLocal原理及设计,从源码层面上分析了ThreadLocal。但由于ThreadLocal设计之初就是为了绑定当前线程,如果希望当前线程的ThreadLocal能够被子线程使用,实现方式就会相当困难(需要用户自己在代码中传递)。在此背景下,InheritableThreadLocal应运而生。

    Inheritable thread-local variables are used in preference to ordinary thread-local variables when the per-thread-attribute being maintained in the variable (e.g., User ID, Transaction ID) must be automatically transmitted to any child threads that are created.

    2、应用

    调用链追踪:在调用链系统设计中,为了优化系统运行速度,会使用多线程编程,为了保证调用链ID能够自然的在多线程间传递,需要考虑ThreadLocal传递问题(大多数系统会使用线程池技术,这已经不仅仅是InheritableThreadLocal能够解决的了,我会在另外一篇文章中介绍相关技术实现)。

    3、InheritableThreadLocal类

    InheritableThreadLocal类重写了ThreadLocal的3个函数:

        /**
         * 该函数在父线程创建子线程,向子线程复制InheritableThreadLocal变量时使用
         */
        protected T childValue(T parentValue) {
            return parentValue;
        }
    
        /**
         * 由于重写了getMap,操作InheritableThreadLocal时,
         * 将只影响Thread类中的inheritableThreadLocals变量,
         * 与threadLocals变量不再有关系
         */
        ThreadLocalMap getMap(Thread t) {
           return t.inheritableThreadLocals;
        }
    
        /**
         * 类似于getMap,操作InheritableThreadLocal时,
         * 将只影响Thread类中的inheritableThreadLocals变量,
         * 与threadLocals变量不再有关系
         */
        void createMap(Thread t, T firstValue) {
            t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
        }
    

    注意:由于重写了getMap()和createMap()两个函数,所以当

    4、线程间传值实现原理

    说到InheritableThreadLocal,还要从Thread类说起:

    public class Thread implements Runnable {
       ......(其他源码)
        /* 
         * 当前线程的ThreadLocalMap,主要存储该线程自身的ThreadLocal
         */
        ThreadLocal.ThreadLocalMap threadLocals = null;
    
        /*
         * InheritableThreadLocal,自父线程集成而来的ThreadLocalMap,
         * 主要用于父子线程间ThreadLocal变量的传递
         * 本文主要讨论的就是这个ThreadLocalMap
         */
        ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
        ......(其他源码)
    }
    

    Thread类中包含 threadLocals 和 inheritableThreadLocals 两个变量,其中 inheritableThreadLocals 即主要存储可自动向子线程中传递的ThreadLocal.ThreadLocalMap。
    接下来看一下父线程创建子线程的流程,我们从最简单的方式说起:

    4.1、用户创建Thread

    Thread thread = new Thread();
    

    4.2、Thread创建

        /**
         * Allocates a new {@code Thread} object. This constructor has the same
         * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
         * {@code (null, null, gname)}, where {@code gname} is a newly generated
         * name. Automatically generated names are of the form
         * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
         */
        public Thread() {
            init(null, null, "Thread-" + nextThreadNum(), 0);
        }
    

    4.3、Thread初始化

        /**
         * 默认情况下,设置inheritThreadLocals可传递
         */
        private void init(ThreadGroup g, Runnable target, String name,
                          long stackSize) {
            init(g, target, name, stackSize, null, true);
        }
    
        /**
         * 初始化一个线程.
         * 此函数有两处调用,
         * 1、上面的 init(),不传AccessControlContext,inheritThreadLocals=true
         * 2、传递AccessControlContext,inheritThreadLocals=false
         */
        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);
    
            ......(其他代码)
        }
    

    可以看到,采用默认方式产生子线程时,inheritThreadLocals=true;若此时父线程inheritableThreadLocals不为空,则将父线程inheritableThreadLocals传递至子线程。

    4.4、ThreadLocal.createInheritedMap

    让我们继续追踪createInheritedMap:

        static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
            return new ThreadLocalMap(parentMap);
        }
    
            /**
             * 构建一个包含所有parentMap中Inheritable ThreadLocals的ThreadLocalMap
             * 该函数只被 createInheritedMap() 调用.
             */
            private ThreadLocalMap(ThreadLocalMap parentMap) {
                Entry[] parentTable = parentMap.table;
                int len = parentTable.length;
                setThreshold(len);
                // ThreadLocalMap 使用 Entry[] table 存储ThreadLocal
                table = new Entry[len];
    
                // 逐一复制 parentMap 的记录
                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) {
                            // 可能会有同学好奇此处为何使用childValue,而不是直接赋值,
                            // 毕竟childValue内部也是直接将e.value返回;
                            // 个人理解,主要为了减轻阅读代码的难度
                            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++;
                        }
                    }
                }
            }
    

    从ThreadLocalMap可知,子线程将parentMap中的所有记录逐一复制至自身线程。

    5、总结

    InheritableThreadLocal主要用于子线程创建时,需要自动继承父线程的ThreadLocal变量,方便必要信息的进一步传递。

  • 相关阅读:
    iOS drewRect方法
    iOS中的单例
    iOS中类别的使用
    iOS存储的三种方式
    安卓上微信闪退的一种解决方法
    [Z] 通天塔导游:各种编程语言的优缺点
    怎样面对痛苦?
    [Z] 10 种必知必会的软件开发工具
    [Z] Linux 内核同步机制
    [Z] 囚禁你的精灵(daemon)进程
  • 原文地址:https://www.cnblogs.com/kelelipeng/p/11613998.html
Copyright © 2011-2022 走看看