前言
先说ThreadLocal吧,最早使用的场景是将用户信息存储在ThreadLocal里面,如果是同一个线程进来就不用重新获取用户信息了,直接从本地线程变量里面获取,在业务的很多地方都需要使用用户信息,存储到ThreadLocal里方便调用,每个线程持有一个ThreadLocalMap对象,Map的key是Threadlocal实例本身,value是threadLocals真正存储的值;
ThreadLocal
ThreadLocal主要作用:
- 线程间的数据隔离
- 存储事务信息
- Session会话管理
csharp
private final static ThreadLocal<String> threadLocal =new ThreadLocal<>();
public static void main(String[] args) {
threadLocal.set("parent thread data");
System.out.println(Thread.currentThread().getName()+"ThreadLocal data:" +threadLocal.get());
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" data:" +threadLocal.get());
}
}).start();
}
子线程无法获取主线程ThreadLocal存的变量
InheritableThreadLocal
如果我们把ThreadLocal换成InheritableThreadLocal
csharp
private final static InheritableThreadLocal<String> threadLocal =new InheritableThreadLocal<>();
public static void main(String[] args) {
threadLocal.set("parent thread data");
System.out.println(Thread.currentThread().getName()+"ThreadLocal data:" +threadLocal.get());
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" data:" +threadLocal.get());
}
}).start();
}
可见子线程就可以访问主线程的变量了
InheritableThreadLocal重写方法
InheritableThreadLocal继承了ThreadLocal方法,并且重写了getMap和 createMap方法
scala
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
//该方法主要是父线程向子线程复制InheritableThreadLocal变量
protected T childValue(T parentValue) {
return parentValue;
}
//重写了ThreadLocal类,现在getMap指向的引用不再是theadlocals而是inheritableThreadLocals
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
//创建ThreadLocalMap,指向inheritableThreadLocals
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
主线程向子线程传递数据
要从Thread类看起,Thread里面有两个变量
Thread的init初始化方法
csharp
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
关键代码,inheritThreadLocals和parent的inheritThreadLocals不为空,这里的parent指的就是主线程,将父线程传递给子线程
createInheritedMap方法
接着来看 createInheritedMap方法,返回了一个ThreadLocalMap;
javascript
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
ThreadLocalMap主要是parentMap的变量逐一复制到子线程
ini
private ThreadLocalMap(ThreadLocalMap parentMap) {
//父线程ThreadLocalMap,获取ThreadLocal存在方式为entry数组
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) {
//这里调用了childValue方法,父线程向子线程复制ThreadLocal变量的方法
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++;
}
}
}
}
InheritableThreadLocal支持线程池之间的传递么?
通过线程之前的变量传递,可以通过InheritableThreadLocal解决,那么开启线程池,在线程池中进行变量的传递共享可以么?
使用单例的线程池,模拟父子线程之前的传递;
ini
ExecutorService executorService = Executors.newSingleThreadExecutor();
InheritableThreadLocal<String> threadLocal =new InheritableThreadLocal<>();
for (int i = 0; i <4 ; i++) {
threadLocal.set("parent thread data -"+i);
Thread.sleep(3000);
CompletableFuture.runAsync(() -> System.out.println(threadLocal.get()), executorService);
}
executorService.shutdownNow();
执行结果,可以看出是如何完成变量共享的,那么如何才能在线程池内完成线程共享呢,下一篇会讲到;
总结
至此就可以得出结论,使用InheritableThreadLocal存储变量,有一步的操作就是将父线程里存储的变量,通过entry数组的方式循环进行传递,这样就实现了线程之前数据的可见性。