一、背景
在实际代码开发中,有些时候,单个线程执行任务非常多的时候,后一个步输入是前一个步骤的输出,若是一直带着一个参数传递,就会显得很冗余,于是就有了线程上下文的设计,每个线程会有不同的参数实例。
二、实现
ThreadLocal作为一个线程私有化的类,内部有一个私有变量ThreadLocalMap,其已每个线程Thread.currentThread()作为key,这样就可以保证线程的独立性。因此我们可以利用ThreadLocal进行线程上下文的构建。
新建一个上下文的类:
import java.io.Serializable;
/**
* @author: liulei
* @date: 2022-04-13 15:42
**/
public class TestContext {
private ThreadLocal<Context> threadLocal;
private TestContext() {
this.threadLocal = new InheritableThreadLocal<>();
}
public void clear() {
this.threadLocal.remove();
}
private static class Singleton {
private static final TestContext testContext = new TestContext();
}
public static TestContext getInstance() {
return Singleton.testContext;
}
public Context getContext() {
if (threadLocal.get() == null) {
threadLocal.set(new Context());
}
return threadLocal.get();
}
public String getName() {
return getContext().getName();
}
public void setName(String name) {
getContext().setName(name);
}
public void setAge(Integer age) {
getContext().setAge(age);
}
public static class Context implements Serializable {
private String name;
private Integer age;
public String getName() {
return this.name;
}
public void setName(String str) {
this.name = str;
}
public void setAge(Integer age) {
this.age = age;
}
}
}
当我们把在某一个线程中把属性set进上下文中,就可以在该线程的调用链中随取随用。
三、一些问题
这里需要强调一点的是:此处我们threadLocal用的InheritableThreadLocal进行实例化,如果直接是用ThreadLocal类进行实例化,在子线程中是获取不到父线程的上下文的,因此这里需要区别使用。
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestContext.getInstance().setName("testName");
CompletableFuture<Void> future = CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread()+" start,time->"+System.currentTimeMillis());
// TestContext.getInstance().setAge(11);
System.out.println(Thread.currentThread()+ TestContext.getInstance().getName());
System.out.println(Thread.currentThread()+" exit,time->"+System.currentTimeMillis());
TestContext.getInstance().clear();
});
System.out.println(Thread.currentThread()+" start,time->"+System.currentTimeMillis());
//等待子任务执行完成
future.get();
System.out.println(Thread.currentThread()+ TestContext.getInstance().getName());
System.out.println(Thread.currentThread()+" exit,time->"+System.currentTimeMillis());
}