较真儿学源码系列-InheritableThreadLocal(逐行源码带你分析作者思路)

        Java版本:8u261。

        之前我写过对ThreadLocal源码进行分析的文章,感兴趣的话可以查看《较真儿学源码系列-ThreadLocal(逐行源码带你分析作者思路)》


        InheritableThreadLocal是ThreadLocal的子类,通过前面的分析可知,ThreadLocal只能在同一线程内进行变量的共享,而InheritableThreadLocal不仅可以在同一线程内进行变量的共享,而且可以在父子线程之间进行变量的共享。比如说在父线程a中创建了一个子线程b,那么在a线程中用InheritableThreadLocal包装的变量,在子线程b中也能获取的到。但需要注意的是:InheritableThreadLocal和ThreadLocal一样,在同级线程中依然不能共享变量的值。并且InheritableThreadLocal只能父传子,不能子传父(同时也不是任何时候都能父传子,只有在一开始初始化的时候才会进行数据传递,后面会看到这点)。

public class InheritableThreadLocal<T> extends ThreadLocal<T> {

    protected T childValue(T parentValue) {
        return parentValue;
    }

    ThreadLocalMap getMap(Thread t) {
        return t.inheritableThreadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

        以上便是InheritableThreadLocal的全部源码,可见其只是覆写了ThreadLocal的三个方法而已。而在getMap和createMap方法中可以看到inheritableThreadLocals这个属性,那么这个属性到底是干什么的呢?其实它跟threadLocals属性一样,都是放在Thread类中的属性:

public class Thread implements Runnable {
    //...

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    //...
}

        正是因为有了inheritableThreadLocals这个属性,就可以让子线程来访问父线程中的本地变量。

        在创建线程的时候,会调用到Thread类的init方法:

/**
 * Thread:
 */
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    //...

    Thread parent = currentThread();
    //...
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        /*
        将父线程中inheritableThreadLocals的数据初始化到一个新的ThreadLocalMap中,
        并赋值给子线程的inheritableThreadLocals
         */
        this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    //...
}

        这里省略了其他不相干逻辑,只需要来看一下inheritableThreadLocals的初始化过程,进一步跟踪第17行代码处:

/**
 * ThreadLocal:
 */
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
    //parentMap是父线程中的数据
    return new ThreadLocalMap(parentMap);
}

/**
 * 将父线程中的inheritableThreadLocals复制到一个新的ThreadLocalMap中
 * 个人认为这里直接将parentMap返回回去应该也是可以的,但这里重新构建一个
 * ThreadLocalMap感觉是为了做一遍清理工作,将Entry为null的哈希槽清理掉
 */
private ThreadLocalMap(ThreadLocalMap parentMap) {
    Entry[] parentTable = parentMap.table;
    int len = parentTable.length;
    //设置子线程的threshold
    setThreshold(len);
    //初始化子线程的table(因为是在子线程创建的时候调用到这里的,所以不需要判断是否已经初始化,这里一定是未初始化的)
    table = new Entry[len];

    //遍历父线程中的table
    for (int j = 0; j < len; j++) {
        //获取其中的Entry
        Entry e = parentTable[j];
        if (e != null) {
            //获取当前槽中的Entry中保存的ThreadLocal
            @SuppressWarnings("unchecked")
            ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
            if (key != null) {
                //这里会调用InheritableThreadLocal覆写的childValue方法,也就是返回e.value本身
                Object value = key.childValue(e.value);
                //构建一个新的Entry
                Entry c = new Entry(key, value);
                //获取哈希槽的位置
                int h = key.threadLocalHashCode & (len - 1);
                //通过线性探测的方式来找到要插入的位置
                while (table[h] != null)
                    h = nextIndex(h, len);
                //插入数据
                table[h] = c;
                //计数+1
                size++;
            }
        }
    }
}

/**
 * InheritableThreadLocal:
 * 第32行代码处:
 */
protected T childValue(T parentValue) {
    return parentValue;
}

        完整的流程如下:

  1. 首先父线程调用set或get方法时,会调用InheritableThreadLocal覆写的getMap方法返回inheritableThreadLocals,但因为其没有初始化,所以会调用覆写的createMap方法来创建ThreadLocalMap,并赋值给inheritableThreadLocals。这样父线程的inheritableThreadLocals属性就不为null了;
  2. 接着父线程会调用set方法往父线程的inheritableThreadLocals属性中的table数组中进行赋值;
  3. 然后创建子线程的时候,会调用Thread类的init方法中的ThreadLocal.createInheritedMap方法。将父线程inheritableThreadLocals属性中的数据初始化到一个新的ThreadLocalMap中,并同时赋值给子线程的inheritableThreadLocals。这样子线程的inheritableThreadLocals属性中就有了父线程中的数据;
  4. 最后子线程在调用get方法的时候就能拿到父线程中的数据了。但是需要注意的是:子线程执行完毕后,父线程此时调用get方法拿到的还是之前父线程中的inheritableThreadLocals,并不是子线程会往其中更改过的ThreadLocalMap。也就是说子线程的数据不会传递给父线程,子线程只有在一开始初始化的时候才会同步父线程中的数据。

原创不易,未得准许,请勿转载,翻版必究

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值