1 前言
上节我们看了下 ThreadLocal 的实现原理,这节我们来看下 InheritableThreadLocal 是用来干什么的呢?
我们首先看个简单的现象:
那我们把 ThreadLocal 换成 InheritableThreadLocal 的再来看下呢:
可以看到我们开辟的新线程里也能获取到了,为什么呢?接下来我们分析下。
2 源码分析
在看源码前我们先来看下 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); } }
可以看到就这么点东西,那他是怎么实现的呢?
2.1 getMap
我们看到 getMap 方法返回的是线程中的 inheritableThreadLocals 变量,我们看下:
可以看到其实就是创建 map 和 getMap 的时候初始化的是线程中的 inheritableThreadLocals 变量。
那么就有点邪门了,main 线程就是 set的时候,设置的也是设置进自己的 main线程里了啊,那么新线程 get 的时候也是从自己的线程中获取,它又怎么能拿到 main线程中的呢?是不是有这样的困惑?那是不是就是创建新线程的时候 jdk 给我动了什么手脚呢?我们看下新线程的创建:
2.2 创建线程过程
// 创建线程 public Thread(Runnable target) { // 调用重载方法 init(null, target, "Thread-" + nextThreadNum(), 0); } // Thread private void init(ThreadGroup g, Runnable target, String name, long stackSize) { // 继续调用重载 看最后一个参数是 默认是true init(g, target, name, stackSize, null, true); } // Thread 最后是这里来创建的 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; // 当前线程是谁? 是不是就是我们的 main线程 Thread parent = currentThread(); ...省略部分代码 setPriority(priority); /** * 看这里 inheritThreadLocals 是不是默认为true * parent.inheritableThreadLocals 是不是不为空 * 我们在创建线程前是不是 set了一个值 是不是就是放进了main线程的 inheritableThreadLocals 参数里 * 这里是不是条件成立 */ if (inheritThreadLocals && parent.inheritableThreadLocals != null) // 是不是就给当前要创建的新线程里,把 main 线程里的值初始化了一份进去 这就是新线程里为什么能拿到 this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; /* Set thread ID */ tid = nextThreadID(); }
这就是 InheritableThreadLocal,就是在当前创建的新线程里,判断父线程里的 inheritableThreadLocals 是不是为空,不为空的话就会复制一份进来。
2.3 createInheritedMap
我们再来看下新线程是如何复制父线程中的:
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { return new ThreadLocalMap(parentMap); }
// ThreadLocalMap private ThreadLocalMap(ThreadLocalMap parentMap) { // 父线程中的ThreadLocalMap 的 table Entry[] parentTable = parentMap.table; // 父线程中的ThreadLocalMap 的 table 的长度 int len = parentTable.length; // 设置当前新 ThreadLocalMap 的 长度 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) { Object value = key.childValue(e.value); // 看这里创建新的 Entry 把 key、value 扔进去 Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] != null) h = nextIndex(h, len); // 设置回新的 ThreadLocalMap table[h] = c; size++; } } } }
可以看到会创建一个新的 ThreadLocalMap 对象,然后把父亲的 ThreadLocalMap 遍历,进行新 Entry的 key、value的初始化,然后赋值给新的 ThreadLocalMap 。
2.3 带来的疑问
就是当我们是值类型传递的时候,是不是当 main线程更新了值,我们的新线程里是不是还是旧值的?我们看看:
引用类型的话,因为值地址传递,都是引用的同一个对象还好:
可以看到引用传递的话父子是可以同步的,值类型的话只会在创建新线程的那一刻和父线程保持一致,后续的更改不会作用到子线程里。所以大家使用的时候注意下。
3 小结
这节我们看了下 InheritableThreadLocal的实现的一个小原理,有理解不对的地方欢迎指正哈。
标签:InheritableThreadLocal,ThreadLocalMap,创建,inheritableThreadLocals,线程,原理,机制,null From: https://www.cnblogs.com/kukuxjx/p/17230605.html