java并发编程基础 (6)

这种ThreadLocal可以从父线程传到子线程,也就是子线程能访问父线程中的InheritableThreadLocal

public class Demo_02_08_2_ThreadLocalInherit { static class TestThreadLocalInherit extends Thread{ @Override public void run() { System.out.println(threadLocal.get()); // null System.out.println(inheritableThreadLocal.get()); // 欢迎关注微信公众号 大雄和你一起学编程 } } public static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>(); public static InheritableThreadLocal<Object> inheritableThreadLocal = new InheritableThreadLocal<>(); public static void main(String[] args) { inheritableThreadLocal.set("欢迎关注微信公众号 大雄和你一起学编程"); threadLocal.set("ddd"); new TestThreadLocalInherit().start(); } } 实现原理

很容易想到,因为这个东西是跟着线程走的,所以应该是线程的一个属性,事实上也是这样,ThreadLocal和InheritableThreadLocal都是存储在Thread里面的。

/* 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;

上边这个就是Thread的两个成员变量,其实两个是一样的类型。

ThreadLocalMap是ThreadLocal的内部类,他里边是一个用一个Entry数组来存数据的。set时将ThreadLocal作为key,要存的值传进去,他会对key做一个hash,构建Entry,放到Entry数组里边。

// 伪码 static class ThreadLocalMap { // 内部的Entry结构 static class Entry {...} // 存数据的 private Entry[] table; // set private void set(ThreadLocal<?> key, Object value) { int i = key.threadLocalHashCode & (len-1); tab[i] = new Entry(key, value); } // get private Entry getEntry(ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); } }

再来看看ThreadLocal的get方法

public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); // 这个就是拿到的存在Thread的threadLocals这个变量 if (map != null) { // 这里就是毫无难度的事情了 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } // 这个也很简单,他会调你重写的initialValue方法,拿到一个值,set进去并且返回给你 // 这个也很有趣,一般init在初始化完成,但是他是在你取的时候去调,应该算是一个小小优化吧 return setInitialValue(); }

再来看看ThreadLocal的set, 超级简单,不多说

public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }

ThreadLocal看完了,再来瞅瞅InheritableThreadLocals,看看他是怎么可以从父线程那里拿东西的

// 继承了ThreadLocal, 重写了三个方法 public class InheritableThreadLocal<T> extends ThreadLocal<T> { // 这个方法在ThreadLocal是直接抛出一个异常UnsupportedOperationException protected T childValue(T parentValue) { return parentValue; } // 超简单,我们的Map不要threadLocals了,改为inheritableThreadLocals ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; } // 同上 void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); } }

发现他和ThreadLocal长得差不多,就是重写了三个方法,由此看来关键在inheritableThreadLocals是如何传递的

直接在Thread里面搜inheritableThreadLocals

你会发现他是在init方法中赋值的,而init实在Thread的构造方法中调用的

// 这个parent就是 创建这个线程的那个线程,也就是父线程 if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

看来现在得看看ThreadLocal.createInheritedMap这个方法了

// parentMap就是父线程的inheritableThreadLocals static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { return new ThreadLocalMap(parentMap); } // 发现很简单,就是把父线程的东西到自己线程的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) { 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++; } } } }

总结一下

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzdsxd.html