异步线程池,保存用户信息

104 阅读1分钟

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());
}

效果

image.png

image.png

注意点

image.png 要使用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);
    }
}