如何保证子线程InheritableThreadLocal可以复用主线程InheritableThreadLocal中的内容
1,ThreadLocal的使用与缺点
ThreadLocal可以保证每个线程获取到的ThreadLocal变量是相互隔离的,我在线程1中获取ThreadLocal对象并set一个值,在线程2中获取的ThreadLocal的对象无法获取到在线程1中set的值,demo如下:
public class MyStudy1014_01 implements Runnable{
private static ThreadLocal<String> mThreadLocal = new ThreadLocal<>();
public static void main(String[] args) {
mThreadLocal.set("主线程mThreadLocal有值了");
System.out.println("获取主线程mThreadLocal中的值 : "+mThreadLocal.get());
Thread t1 = new Thread(new MyStudy1014_01());
t1.start();
}
@Override
public void run() {
System.out.println("子线程中获取mThreadLocal的值"+mThreadLocal.get());
}
}
}
}
运行结果如下, 子线程中获取的ThreadLocal中的值为空
2,InheritableThreadLocal弥补ThreadLocal的缺点
上面的案例中,我们将ThreadLocal换成InheritableThreadLocal,再次执行
public class MyStudy1014_02 implements Runnable{
private static InheritableThreadLocal<String> mInheritableThreadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
mInheritableThreadLocal.set("主线程mInheritableThreadLocal有值了");
System.out.println("获取主线程mInheritableThreadLocal中的值 : "+mInheritableThreadLocal.get());
Thread t1 = new Thread(new MyStudy1014_02());
t1.start();
}
@Override
public void run() {
System.out.println("子线程中获取mInheritableThreadLocal的值"+mInheritableThreadLocal.get());
}
}
执行结果如下,可以看到,子线程获取的InheritableThreadLocal 对象可以get到在主线程中set的值
3,InheritableThreadLocal弥补ThreadLocal的缺点的原理
使用idea快捷键alt + 7 查看InheritableThreadLocal的类结构,发现它只重写了ThreadLocal的childValue(T), getMap(T)以及createMap(Thread,T)3个方法
对比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);
}
}
T childValue(T parentValue) {
throw new UnsupportedOperationException();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
我们都知道ThreadLocal在set以及get时,实际执行的是当前Thread对象中ThreadLocalMap的方法,这里可参照ThreadLocal的源码也可参照这篇博客(点此跳转)。所以问题出在getMap的返回值,可以看到ThreadLocal返回的是Thread.threadLocals, 而InheritableThreadLocal返回的是Thread.inheritableThreadLocals。继续查看Thread下这两个变量的区别。
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
可以发现当Thread对象初始化的时候,注意这个时候还在主线程进行对象的初始化,会将主线程的inheritableThreadLocals对象赋值给新创建的Thread对象中的inheritableThreadLocals变量,所以当Thread.start()的时候,会拿到父线程也就是主线程mInheritableThreadLocal对象中的值
4,注意,由于拷贝父线程中mInheritableThreadLocal对象发生在Thread对象初始化时期,所以线程池与mInheritableThreadLocal搭配通常是不好用的,因为线程池复用技术不会初始化Thread对象
实例如下:
public class MyStudy1014_03 {
private static InheritableThreadLocal<String> mInheritableThreadLocal = new InheritableThreadLocal<>();
private static ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(1);
public static void main(String[] args) {
mInheritableThreadLocal.set("主线程第一次set值");
System.out.println("主线程mInheritableThreadLocal第一次get"+mInheritableThreadLocal.get());
newFixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("子线程mInheritableThreadLocal第一次get"+mInheritableThreadLocal.get());
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mInheritableThreadLocal.set("主线程第二次set值");
System.out.println("主线程mInheritableThreadLocal第二次get"+mInheritableThreadLocal.get());
newFixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("子线程mInheritableThreadLocal第二次get"+mInheritableThreadLocal.get());
}
});
newFixedThreadPool.shutdown();
}
}
第二次线程池获取mInheritableThreadLocal中的值仍然是第一次中的值