阅读本文前,需要了解
① ThreadLocal的特性,参考:Java随笔1-ThreadLocal
② Inheritable:adj. 可继承的;会遗传的
1、特性
相比ThreadLocal,InheritableThreadLocal有什么特性?可从以下demo窥探一二:
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
public class InheritableTLTest {
InheritableThreadLocal<String> inheritableTL = new InheritableThreadLocal<>();
ThreadLocal<String> threadLocal = new ThreadLocal<>();
@Test
public void compare() throws InterruptedException {
threadLocal.set("祖先1");
inheritableTL.set("祖先2");
CountDownLatch latch = new CountDownLatch(2);
new Thread(() -> {
System.out.println("threadLocal, 儿子线程取出:" + threadLocal.get());
System.out.println("inheritableTL, 儿子线程取出:" + inheritableTL.get() + "\n");
threadLocal.set("儿子1");
inheritableTL.set("儿子2");
latch.countDown();
new Thread(() -> {
System.out.println("threadLocal, 孙子线程取出:" + threadLocal.get());
System.out.println("inheritableTL, 孙子线程取出:" + inheritableTL.get() + "\n");
threadLocal.set("孙子1");
inheritableTL.set("孙子2");
latch.countDown();
}).start();
}).start();
latch.await();
System.out.println("threadLocal, 主线程取出:" + threadLocal.get());
System.out.println("inheritableTL, 主线程取出:" + inheritableTL.get());
}
}
demo执行结果如下:
threadLocal, 儿子线程取出:null
inheritableTL, 儿子线程取出:祖先2
threadLocal, 孙子线程取出:null
inheritableTL, 孙子线程取出:儿子2
threadLocal, 主线程取出:祖先1
inheritableTL, 主线程取出:祖先2
从结果看出,主线程设置了“祖先信息”,能通过InheritableThreadLocal传递给了儿子线程,无法通过ThreadLocal传递给儿子线程;儿子线程与孙子线程之间的传递同样如此。另外,儿子、孙子线程修改了变量,并不影响最后的主线程拿到最初设置的信息。
结论:相比ThreadLocal,InheritableThreadLocal可以在父子线程之间传递数据。
2、原理
Thread部分源码如下:
public class Thread implements Runnable {
private Runnable target;
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
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;
//父线程
Thread parent = currentThread();
...
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
}
}
可以看出,线程类有2个属性:threadLocals、inheritableThreadLocals,父线程使用new Thread(Runnable target)创建子线程的时候,会拷贝他的inheritableThreadLocals赋值给儿子线程的inheritableThreadLocals。
在调用ThreadLocal的get()方法时,ThreadLocal类的getMap()方法返回了线程的threadLocals属性,inheritableThreadLocals覆盖了getMap()方法返回了线程的inheritableThreadLocals属性。