一、InheritableThreadLocal简介

由于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.

二、InheritableThreadLocal应用

分布式链路追踪系统

如果我们需要记录系统的每个调用链路,那么每一个子系统里面,如果调用了异步线程来做处理的话,类似这种链路是不是需要收集起来呢? 在调用链系统设计中,为了优化系统运行速度,会使用多线程编程,为了保证调用链ID能够自然的在多线程间传递,需要考虑ThreadLocal传递问题(大多数系统会使用线程池技术,这已经不仅仅是InheritableThreadLocal能够解决的了(使用TransmittableThreadLocal解决池化复用线程的传值问题))

日志收集记录系统

上下文在实际的日志打印记录中,一个http请求进来的话,每一行日志,日志产生的线程信息?上下文信息,是不是需要记录下来呢?

三、与ThreadLocal存储数据的区别

  1. /* ThreadLocal values pertaining to this thread. This map is maintained
  2. * by the ThreadLocal class.
  3. * 与该线程相关的ThreadLocal值。 这个映射由ThreadLocal类维护。
  4. */
  5. ThreadLocal.ThreadLocalMap threadLocals = null;
  6. /*
  7. * InheritableThreadLocal values pertaining to this thread. This map is
  8. * maintained by the InheritableThreadLocal class.
  9. * 与该线程相关的InheritableThreadLocal值。 这个映射由InheritableThreadLocal类维护。
  10. */
  11. ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

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()两个函数,所以与threadLocals变量不再有关系

四、线程间传值实现原理

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

public class Thread implements Runnable {
   ......(其他源码)
    /* 
     * 当前线程的ThreadLocalMap,主要存储该线程自身的ThreadLocal
     */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal,自父线程集成而来的ThreadLocalMap,
     * 主要用于父子线程间ThreadLocal变量的传递
     * 本文主要讨论的就是这个ThreadLocalMap
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    ......(其他源码)
}

Thread类中包含 threadLocalsinheritableThreadLocals 两个变量,其中 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中的所有记录逐一复制至自身线程。

五、总结

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

六、存在的问题

JDK 的InheritableThreadLocal类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已经没有意义,应用需要的实际上是把任务提交给线程池时的ThreadLocal值传递到任务执行时
这个问题在下一章 TransmittableThreadLocal 会得到解决