当前位置: 首页 > article >正文

java异步线程之间数据传递

文章目录

    • 1、手动设置
    • 2、线程池设置TaskDecorator(推荐)
    • 3、InheritableThreadLocal
    • 4、TransmittableThreadLocal(推荐)

/**
 * @description 用户上下文信息
 */
public class OauthContext {
    private static  final  ThreadLocal<LoginVal> loginValThreadLocal=new ThreadLocal<>();

    public static  LoginVal get(){
        return loginValThreadLocal.get();
    }
    public static void set(LoginVal loginVal){
        loginValThreadLocal.set(loginVal);
    }
    public static void clear(){
        loginValThreadLocal.remove();
    }
}

1、手动设置

每执行一次异步线程都要分为两步:

  1. 获取父线程的LoginVal
  2. 将LoginVal设置到子线程,达到复用
public void handlerAsync() {
        //1. 获取父线程的loginVal
        LoginVal loginVal = OauthContext.get();
        log.info("父线程的值:{}",OauthContext.get());
        CompletableFuture.runAsync(()->{
            //2. 设置子线程的值,复用
           OauthContext.set(loginVal);
           log.info("子线程的值:{}",OauthContext.get());
        });
}

每次开异步线程都需要手动设置,重复代码太多,看了头疼

2、线程池设置TaskDecorator(推荐)

TaskDecorator这是一个执行回调方法的装饰器,主要应用于传递上下文,或者提供任务的监控/统计信息。

TaskDecorator是一个接口,首先需要去实现它,代码如下:

/**
 * @description 上下文装饰器
 */
public class ContextTaskDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        //获取父线程的loginVal
        LoginVal loginVal = OauthContext.get();
        return () -> {
            try {
                // 将主线程的请求信息,设置到子线程中
                OauthContext.set(loginVal);
                // 执行子线程,这一步不要忘了
                runnable.run();
            } finally {
                // 线程结束,清空这些信息,否则可能造成内存泄漏
                OauthContext.clear();
            }
        };
    }
}

这里只是设置了LoginVal,实际开发中其他的共享数据,比如SecurityContext,RequestAttributes…
TaskDecorator需要结合线程池使用,实际开发中异步线程建议使用线程池,只需要在对应的线程池配置一下,代码如下:

@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
        poolTaskExecutor.setCorePoolSize(xx);
        poolTaskExecutor.setMaxPoolSize(xx);
        // 设置线程活跃时间(秒)
        poolTaskExecutor.setKeepAliveSeconds(xx);
        // 设置队列容量
        poolTaskExecutor.setQueueCapacity(xx);
        //设置TaskDecorator,用于解决父子线程间的数据复用
        poolTaskExecutor.setTaskDecorator(new ContextTaskDecorator());
        poolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        poolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        return poolTaskExecutor;
}

此时业务代码就不需要去设置子线程的值,直接使用即可,代码如下:

public void handlerAsync() {
        log.info("父线程的用户信息:{}", OauthContext.get());
        //执行异步任务,需要指定的线程池
        CompletableFuture.runAsync(()-> log.info("子线程的用户信息:{}", OauthContext.get()),taskExecutor);
}

CompletableFuture执行异步任务,使用@Async这个注解同样是可行的

3、InheritableThreadLocal

这种方案不建议使用,InheritableThreadLocal虽然能够实现父子线程间的复用,但是在线程池中使用会存在复用的问题

这种方案使用也是非常简单,直接用InheritableThreadLocal替换ThreadLocal即可,代码如下:

/**
 * @description 用户上下文信息
 */
public class OauthContext {
    private static  final  InheritableThreadLocal<LoginVal> loginValThreadLocal=new InheritableThreadLocal<>();

    public static  LoginVal get(){
        return loginValThreadLocal.get();
    }
    public static void set(LoginVal loginVal){
        loginValThreadLocal.set(loginVal);
    }
    public static void clear(){
        loginValThreadLocal.remove();
    }
}

4、TransmittableThreadLocal(推荐)

TransmittableThreadLocal是阿里开源的工具,弥补了InheritableThreadLocal的缺陷,在使用线程池等会池化复用线程的执行组件情况下,提供ThreadLocal值的传递功能,解决异步执行时上下文传递的问题。
使用起来也是非常简单,添加依赖如下:

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>transmittable-thread-local</artifactId>
  <version>2.14.2</version>
</dependency>

OauthContext改造代码如下:

/**
 * @description 用户上下文信息
 */
public class OauthContext {
    private static  final TransmittableThreadLocal<LoginVal> loginValThreadLocal=new TransmittableThreadLocal<>();

    public static  LoginVal get(){
        return loginValThreadLocal.get();
    }
    public static void set(LoginVal loginVal){
        loginValThreadLocal.set(loginVal);
    }
    public static void clear(){
        loginValThreadLocal.remove();
    }
}


http://www.kler.cn/a/37184.html

相关文章:

  • JavaScript 深度剖析-函数式编程(一)
  • 基于 OpenCV 的图像处理与分析应用的设计与实现
  • 【牛客面试真题】字符串操作
  • 基于单片机心率脉搏心率血压体温血氧检测系统的设计与实现
  • Shell第一章——Shell编程规范与变量
  • SpringBoot前后端分离项目,打包、部署到服务器详细图文流程
  • RestHighLevelClient集成ES 7.X
  • Vue-Router(4) 学习之动态路由二
  • 与君共勉之!
  • 用ChatGPT解析Wireshark抓取的数据包样例
  • Java+Vue+Uniapp全端WMS仓库管理系统
  • 机器学习——决策树1(三种算法)
  • JS--es6模板字符串
  • ModaHub魔搭社区:AI原生云向量数据库Zilliz Cloud与 LangChain 集成搭建智能文档问答系统
  • elementUI 表格页面层级嵌套过多不及时刷新/错位的解决办法
  • 一文了解Docker之网络模型
  • 什么是用电信息采集系统?
  • C# SQL代码字符拼接
  • DaVinci Resolve Studio 18对Mac和Windows系统的要求
  • NB-IoT模块(BC系列—BC95)详解