本地线程变量(二):InheritableThreadLocal

InheritableThreadLocal

一、简介

​ 在Thread中除了有属性threadLocals引用ThreadLocal.ThreadLocalMap类,其实还有一个属性,也就是inheritableThreadLocalsthreadLocals的作用是保存本地线程变量,而inneritableThreadLocals的作用是传递当前线程本地变量InheritableThreadLocal到子线程的本地变量InheritableThreadLocal中。

二、实例
public static void main(String[] args) throws InterruptedException {
  InheritableThreadLocal<String> username = new InheritableThreadLocal<>();
  ThreadLocal<String> password = new ThreadLocal<>();
  username.set("zhangShang");
  password.set("123456789");

  new Thread(new Runnable() {
    @Override
    public void run() {
      System.out.println(username.get());
      System.out.println(password.get());
    }
  }).start();
}

输出结果为:

zhangShang
null

所以基本上可以得出结论:InheritableThreadLocal是具有父子线程传递的,而ThreadLocal不具有父子线程传递的功能。

三、原理
1. InheritableThreadLocal的实现

InheritableThreadLocal继承于ThreadLocal,并重写了ThreadLocal中的三个方法。

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    /**
     * 这个接口是ThreadLocal的开放接口,默认实现是抛出UnsupportedOperationException异常。
     * 实现上仅返回入参,调用上是在创建子线程时使用。
     */
    protected T childValue(T parentValue) {
        return parentValue;
    }

    /**
     * 重写getMap,操作InheritableThreadLocal时,将只会影响到线程对象Thread的
     * inheritableThread属性。
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    /**
     * 与上面的获取方法getMap情况一致,创建时同理。
     */
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}
2.线程的创建过程

跟踪new Thread()方法。

1.进入初始化方法。

public Thread() {
  init(null, null, "Thread-" + nextThreadNum(), 0);
}

2.调用init方法。

// 重载方法
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 (name == null) {
    throw new NullPointerException("name cannot be null");
  }
  this.name = name;
  // parent线程为创建子线程的当前线程,也就是父线程。
  Thread parent = currentThread();
  
	... 省略一些与本章无关代码
		
  // inheritThreadLocals=true,默认值是true,且父线程的inheritableThreadLocal对象不为空
  // 创建当前线程的inheritableThreadLocals对象。    
  if (inheritThreadLocals && parent.inheritableThreadLocals != null)
    this.inheritableThreadLocals =
    ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  /* Stash the specified stack size in case the VM cares */
  this.stackSize = stackSize;

  /* Set thread ID */
  tid = nextThreadID();
}

3.进入创建方法createInheritedMap方法。

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
  // 以父线程的inheritableThreadLocals为实例创建一个ThreadLocalMap对象
  return new ThreadLocalMap(parentMap);
}

/**
 * 以父线程的inheritableThreadLocals为实例创建子线程的inheritableThreadLocals对象
 * 实现上比较简单,将父线程的inheritableThreadLocals循环拷贝给子线程。
 */
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 e = parentTable[j];
    if (e != null) {
      @SuppressWarnings("unchecked")
      ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
      if (key != null) {
        // 至于这个地方为什么采用key.childValue(),内层的逻辑也仅仅是返回入参。
        // 网上有些人说是为了减轻代码的阅读难度,笔者觉得有点牵强。感觉是为了在获取
        // 过程中做一些小转换之类的?
        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++;
      }
    }
  }
}

四、思考

​ 上面说父子进程通过inheritableThreadLocals属性来传递本地变量,在实际的应用场景中,一般不会出现父进程直接创建子进程的情况,一般都是采用线程池的方式,如果采用线程池那么inheritableThreadLocal还会有效吗?读者可以考虑一下,写个demo跑一下,看看具体的情况,下一篇文章将进行解答。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值