threadlocal
业务场景中,常用threadlocal保持上下文,如用户信息传递,也可以在dao层增加切面,处理表字段:create_by、update_by。
多线程场景
多线程场景下,会丢失threadlocal存储的信息,常用inheritableThreadLocal,但是线程池场景下会发生数据混乱问题。 常用transmitableThreadlocal解决
demo
引入alibaba的依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.14.2</version>
</dependency>
定义好上下文:
public class Context {
private static final ThreadLocal<Object> context = new TransmittableThreadLocal<>();
public static void set(Object value) {
context.set(value);
}
public static Object get() {
return context.get();
}
public static void clear() {
context.remove();
}
}
使用 定义线程池
@Bean("threadPool")
public ExecutorService init() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1
, 1
, 60
, TimeUnit.SECONDS
, new ArrayBlockingQueue<>(10)
, new CustomizableThreadFactory("zhangyongnian"));
return TtlExecutors.getTtlExecutorService(executor);
}
设置controller
@PostMapping("/testString")
public Result test2(String body) {
Context.set(body);
log.info("主线程设置{}", Context.get());
userService.async();
log.info("主线程获取{}", Context.get());
return Result.success();
}
service
@Async("threadPool")
public void async() {
log.info("子线程获取,{}", Context.get());
}
效果
注意点
要使用ttl的,因为重写了executorService提交任务方法。
原理
@Override
public Future<?> submit(@NonNull Runnable task) {
return executorService.submit(TtlRunnable.get(task));
}
private TtlRunnable(@NonNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
//保存threadlocal里的值,其实就是一个快照
this.capturedRef = new AtomicReference<Object>(capture());
this.runnable = runnable;
this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}
@Override
public void run() {
//拿到保存的快照信息
Object captured = capturedRef.get();
if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
throw new IllegalStateException("TTL value reference is released after run!");
}
//把captured数据添加到threadlocal中
Object backup = replay(captured);
try {
runnable.run();
} finally {
//replay(captured) 的反向操作
restore(backup);
}
}