InheritableThreadLocal详解

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创建

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变量,方便必要信息的进一步传递。

转载于:https://my.oschina.net/yiqifendou/blog/3102224

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值