特点:可以实现父子线程的变量值传递,对于线程池,就没法传递了(因为线程池中存在线程复用的情况,并不会随着调用一直创建,如果要在线程池中实现变量传递,可以使用阿里的TTL线程池)
原理:线程在创建的时候会执行init(),将父线程的inheritableThreadLocal拷贝过来,进而实现变量值传递的目的
以下的具体的源码分析:
- inheritableThreadLocal继承了ThreadLocal并重写了以下三个方法
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);
}
}
- Thread线程创建时会调用init方法,该方法内会将父线程的inheritableThreadLocalMap进行拷贝,从而子线程能获取父线程传递的变量值
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
// ...
// 因为子线程是父线程创建的,所以currentThread拿到的就是父线程的信息
Thread parent = currentThread();
// ...
// 如果父线程的inheritableThreadLocals不为空,则创建一个threadLocalMap,将父线程threadLocalMap的数据复制到子线程中
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
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) {
// 这里就是把父线程的threadLocalMap中的kv数据填充到子线程中
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直接把值返回,没有做特殊的深拷贝处理
protected T childValue(T parentValue) {
return parentValue;
}
注意:
inheritableThreadLocal父线程值传递的时候是直接把变量赋值给子线程的,如果值是引用类型,那么子线程的修改会导致父线程的值也发生变更的;如果是普通类型的变量,则可以认为数据是相互隔离的
实例demo:
public class InheritableDemo1 {
static InheritableThreadLocal<Man> itl = new InheritableThreadLocal<>();
static InheritableThreadLocal<String> itl1 = new InheritableThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
Man man = new Man();
man.setAge(18);
man.setName("张三");
itl.set(man);
itl1.set("hello");
System.out.println("修改前" + Thread.currentThread().getName() + "线程对应的itl:" + itl.get());
System.out.println("修改前" + Thread.currentThread().getName() +"线程对应的itl1:" + itl1.get());
// 创建子线程
new Thread(() -> {
Man man1 = itl.get();
System.out.println("子线程修改前itl:" + Thread.currentThread().getName() + ":" + itl.get());
System.out.println("子线程修改前itl1:" + Thread.currentThread().getName() + ":" + itl1.get());
man1.setName("李四");
itl1.set("world");
System.out.println("子线程修改后itl:" + Thread.currentThread().getName() + ":" + itl.get());
System.out.println( "子线程修改后itl1:" + Thread.currentThread().getName() + ":" + itl1.get());
}).start();
Thread.sleep(1000);
// inheritableThreadLocal父线程值传递的时候是直接把变量赋值给子线程的,如果值是引用类型,那么子线程的修改会导致父线程的值也发生变更的
System.out.println("修改后" + Thread.currentThread().getName() + "线程对应的itl:" + itl.get());
System.out.println("修改后" +Thread.currentThread().getName() +"线程对应的itl1:" + itl1.get());
// 修改前main线程对应的itl:Man(name=张三, age=18)
// 修改前main线程对应的itl1:hello
// 子线程修改前itl:Thread-0:Man(name=张三, age=18)
// 子线程修改前itl1:Thread-0:hello
// 子线程修改后itl:Thread-0:Man(name=李四, age=18)
// 子线程修改后itl1:Thread-0:world
// 修改后main线程对应的itl:Man(name=李四, age=18)
// 修改后main线程对应的itl1:hello
}
}
因为子线程的修改,父线程的ThreadLocal值已发生变更