父线程变量需要传递到子线程使用一种方式InheritableThreadLocal和TransmittableThreadLocal
作者:互联网
前言
ThreadLocal是相对于每一个线程自己使用的本地变量,有这样的一种需求:父线程生成的变量需要传递到子线程中进行使用。需要使用新的InheritableThreadLocal类来实现,但
InheritableThreadLocal的使用利用Thread 的初始化。我们经常在使用线程的时候,都是使用线程池的方式这个时候就需要TransmittableThreadLocal登场来解决。
实践
一、InheritableThreadLocal使用
public class LocalThreadUtil {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
threadLocal.set("mian threadLocal");
inheritableThreadLocal.set("mian inheritableThreadLocal");
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threadLocal is data is =====" + threadLocal.get());
System.out.println("inheritableThreadLocal is data is =====" + inheritableThreadLocal.get());
}
}).start();
}
}
线程输出:
threadLocal is data is =====null
inheritableThreadLocal is data is =====mian inheritableThreadLocal
可以看到inheritableThreadLocal 在子线程中能够获取到父线程的设置的值,我们可以探究一下这个如何做到的。
原理:
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
//这个是在初始化 thread 中的InheritableThreadLocal 成员变量初始化获取中根据父类值
//获取子类的值,返回和父类一摸一样所以有了地址拷贝
protected T childValue(T parentValue) {
return parentValue;
}
//threadLocal 中的 ThreadLocalMap getMap(Thread t) {return t.threadLocals;}
//是调用线程t.threadLocals; 那说明使用 inheritableThreadLocal 对inheritableThreadLocals 进行初始化操作
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
可以看到InheritableThreadLocal是借助父类的ThreadLocal的能力,继承自ThreadLocal,并且重写了父类的方法:createMap(),getMap(),childValue();Thread 中有两个ThreadLocal和InheritableThreadLocal成员变量,数据类型都是ThreadLocal.ThreadLocalMap 当我们new Thread 新线程调用初始化,操作了成员变量
ThreadLocal 和 InheritableThreadLocal
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
/**
* Initializes a Thread with the current AccessControlContext.
* @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
// 最后一个true 需要初始化InheritableThreadLocal 成员变量
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();
...(省略部分)
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
// inheritThreadLocals 是在初始化传入的,表示需要初始化inheritableThreadLocals
//parent.inheritableThreadLocals != null 初始化线程中有主线程有InheritableThreadLocal这个变量
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
//这个是对copy 主线程中parent.inheritableThreadLocals变量
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
ThreadLocal.createInheritedMap中调用 具体实现方法,对主线程中ThreadLocalMap 进行数据拷贝
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) {
//这个也是InheritableThreadLocal 重写子线程,直接返回父线程的值
//也可protected T childValue(T parentValue) {return parentValue } 中看出这里浅拷贝
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父传子的拷贝过程中非常依赖
new Thread 这个方法,因为这个调用初始化方法对子线程的init方法对thread 中InheritableThreadLocal的进行初始化操作。我们可以验证一下,但线程不进行初始化是否这个还有用。
public class LocalThreadUtil {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
private static TransmittableThreadLocal<String> transmittableThreadLocal = new TransmittableThreadLocal<>();
private static ExecutorService executorService = Executors.newSingleThreadExecutor();
public static void main(String[] args) throws InterruptedException {
inheritableThreadLocal.set("old");
CountDownLatch countDownLatch = new CountDownLatch(1);
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("first children =====" + inheritableThreadLocal.get());
countDownLatch.countDown();
}
});
countDownLatch.await();
// 改变inheritableThreadLocal 的值
inheritableThreadLocal.set("update");
CountDownLatch countDownLatch2 = new CountDownLatch(1);
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("second children =====" + inheritableThreadLocal.get());
countDownLatch2.countDown();
}
});
countDownLatch2.await();
System.out.println("main is ==="+inheritableThreadLocal.get());
}
}
我这里使用newSingleThreadExecutor 只有一个线程,那么在第二次改变inheritableThreadLocal 值再去看执行一个线程的时候就会采用刚刚已经初始化好的线程。结果也证明当线程不再初始化的时候,获取到的值还是线程第一次出化好的值,
first children =====old
second children =====old
main is ===update
一、TransmittableThreadLocal使用
maven 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.2.0</version>
</dependency>
代码的处理
public class LocalThreadUtil {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
private static TransmittableThreadLocal<String> transmittableThreadLocal = new TransmittableThreadLocal<>();
private static ExecutorService executorService = Executors.newSingleThreadExecutor();
public static void main(String[] args) throws InterruptedException {
transmittableThreadLocal.set("old");
CountDownLatch countDownLatch = new CountDownLatch(1);
//这里执行的 是TtlRunnable
executorService.execute(TtlRunnable.get(new Runnable() {
@Override
public void run() {
System.out.println("first children =====" + transmittableThreadLocal.get());
countDownLatch.countDown();
}
}));
countDownLatch.await();
transmittableThreadLocal.set("update");
CountDownLatch countDownLatch2 = new CountDownLatch(1);
executorService.execute(TtlRunnable.get(new Runnable() {
@Override
public void run() {
System.out.println("second children =====" + transmittableThreadLocal.get());
countDownLatch2.countDown();
}
}));
countDownLatch2.await();
System.out.println("main is ==="+transmittableThreadLocal.get());
}
}
打印结果:可以考到当主线程中设置的值改变时候,子线程也同样发生了改变。需要注意的是TransmittableThreadLocal 的使用需要TtlRunnable 的配合使用,才能达到使用效果。
first children =====old
second children =====update
main is ===update
标签:线程,到子,inheritableThreadLocal,ThreadLocal,InheritableThreadLocal,TransmittableThr 来源: https://blog.csdn.net/fajing_feiyue/article/details/113785780