SpringBoot父子线程数据传递的五种方案介绍

 更新时间:2022年09月19日 17:05:23   作者:π大星的日常  
在实际开发过程中我们需要父子之间传递一些数据,比如用户信息等。该文章从5种解决方案解决父子之间数据传递困扰,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

方案1.ThreadLocal+TaskDecorator

用户工具类 UserUtils

/**
 *使用ThreadLocal存储共享的数据变量,如登录的用户信息
 */
public class UserUtils {
    private static  final  ThreadLocal<String> userLocal=new ThreadLocal<>();
    public static  String getUserId(){
        return userLocal.get();
    }
    public static void setUserId(String userId){
        userLocal.set(userId);
    }
    public static void clear(){
        userLocal.remove();
    }
}

自定义CustomTaskDecorator

/**
 * 线程池修饰类
 */
public class CustomTaskDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        // 获取主线程中的请求信息(我们的用户信息也放在里面)
        String robotId = UserUtils.getUserId();
        System.out.println(robotId);
        return () -> {
            try {
                // 将主线程的请求信息,设置到子线程中
                UserUtils.setUserId(robotId);
                // 执行子线程,这一步不要忘了
                runnable.run();
            } finally {
                // 线程结束,清空这些信息,否则可能造成内存泄漏
                UserUtils.clear();
            }
        };
    }
}

ExecutorConfig

在原来的基础上增加 executor.setTaskDecorator(new CustomTaskDecorator());

@Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        log.info("start asyncServiceExecutor----------------");
        //ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //使用可视化运行状态的线程池
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //增加线程池修饰类
        executor.setTaskDecorator(new CustomTaskDecorator());
        //增加MDC的线程池修饰类
        //executor.setTaskDecorator(new MDCTaskDecorator());
        //执行初始化
        executor.initialize();
        log.info("end asyncServiceExecutor------------");
        return executor;
    }

AsyncServiceImpl

    /**
     * 使用ThreadLocal方式传递
     * 带有返回值
     * @throws InterruptedException
     */
    @Async("asyncServiceExecutor")
    public CompletableFuture<String> executeValueAsync2() throws InterruptedException {
        log.info("start executeValueAsync");
        System.out.println("异步线程执行返回结果......+");
        log.info("end executeValueAsync");
        return CompletableFuture.completedFuture(UserUtils.getUserId());
    }

Test2Controller

    /**
     * 使用ThreadLocal+TaskDecorator的方式
     * @return
     * @throws InterruptedException
     * @throws ExecutionException
     */
    @GetMapping("/test2")
    public String test2() throws InterruptedException, ExecutionException {
        UserUtils.setUserId("123456");
        CompletableFuture<String> completableFuture = asyncService.executeValueAsync2();
        String s = completableFuture.get();
        return s;
    }

方案2.RequestContextHolder+TaskDecorator

自定义CustomTaskDecorator

/**
 * 线程池修饰类
 */
public class CustomTaskDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        // 获取主线程中的请求信息(我们的用户信息也放在里面)
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return () -> {
            try {
                // 将主线程的请求信息,设置到子线程中
                RequestContextHolder.setRequestAttributes(attributes);
                // 执行子线程,这一步不要忘了
                runnable.run();
            } finally {
                // 线程结束,清空这些信息,否则可能造成内存泄漏
                RequestContextHolder.resetRequestAttributes();
            }
        };
    }
}

ExecutorConfig

在原来的基础上增加 executor.setTaskDecorator(new CustomTaskDecorator());

@Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        log.info("start asyncServiceExecutor----------------");
        //ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //使用可视化运行状态的线程池
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //增加线程池修饰类
        executor.setTaskDecorator(new CustomTaskDecorator());
        //增加MDC的线程池修饰类
        //executor.setTaskDecorator(new MDCTaskDecorator());
        //执行初始化
        executor.initialize();
        log.info("end asyncServiceExecutor------------");
        return executor;
    }

AsyncServiceImpl

     /**
     * 使用RequestAttributes获取主线程传递的数据
     * @return
     * @throws InterruptedException
     */
    @Async("asyncServiceExecutor")
    public CompletableFuture<String> executeValueAsync3() throws InterruptedException {
        log.info("start executeValueAsync");
        System.out.println("异步线程执行返回结果......+");
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        Object userId = attributes.getAttribute("userId", 0);
        log.info("end executeValueAsync");
        return CompletableFuture.completedFuture(userId.toString());
    }

Test2Controller

    /**
     * RequestContextHolder+TaskDecorator的方式
     * @return
     * @throws InterruptedException
     * @throws ExecutionException
     */
    @GetMapping("/test3")
    public String test3() throws InterruptedException, ExecutionException {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        attributes.setAttribute("userId","123456",0);
        CompletableFuture<String> completableFuture = asyncService.executeValueAsync3();
        String s = completableFuture.get();
        return s;
    }

方案3.MDC+TaskDecorator

自定义MDCTaskDecorator

/**
 * 线程池修饰类
 */
public class MDCTaskDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        // 获取主线程中的请求信息(我们的用户信息也放在里面)
        String userId = MDC.get("userId");
        Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
        System.out.println(copyOfContextMap);
        return () -> {
            try {
                // 将主线程的请求信息,设置到子线程中
                MDC.put("userId",userId);
                // 执行子线程,这一步不要忘了
                runnable.run();
            } finally {
                // 线程结束,清空这些信息,否则可能造成内存泄漏
                MDC.clear();
            }
        };
    }
}

ExecutorConfig

在原来的基础上增加 executor.setTaskDecorator(new MDCTaskDecorator());

@Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        log.info("start asyncServiceExecutor----------------");
        //ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //使用可视化运行状态的线程池
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //增加MDC的线程池修饰类
        executor.setTaskDecorator(new MDCTaskDecorator());
        //执行初始化
        executor.initialize();
        log.info("end asyncServiceExecutor------------");
        return executor;
    }

AsyncServiceImpl

         /**
     * 使用MDC获取主线程传递的数据
     * @return
     * @throws InterruptedException
     */
    @Async("asyncServiceExecutor")
    public CompletableFuture<String> executeValueAsync5() throws InterruptedException {
        log.info("start executeValueAsync");
        System.out.println("异步线程执行返回结果......+");
        log.info("end executeValueAsync");
        return CompletableFuture.completedFuture(MDC.get("userId"));
    }

Test2Controller

     /**
     * 使用MDC+TaskDecorator方式
     * 本质也是ThreadLocal+TaskDecorator方式
     * @return
     * @throws InterruptedException
     * @throws ExecutionException
     */
    @GetMapping("/test5")
    public String test5() throws InterruptedException, ExecutionException {
        MDC.put("userId","123456");
        CompletableFuture<String> completableFuture = asyncService.executeValueAsync5();
        String s = completableFuture.get();
        return s;
    }

方案4.InheritableThreadLocal

用户工具类 UserInheritableUtils

//**
 *使用InheritableThreadLocal存储线程之间共享的数据变量,如登录的用户信息
 */
public class UserInheritableUtils {
    private static  final  InheritableThreadLocal<String> userLocal=new InheritableThreadLocal<>();
 
    public static  String getUserId(){
        return userLocal.get();
    }
    public static void setUserId(String userId){
        userLocal.set(userId);
    }
    public static void clear(){
        userLocal.remove();
    }
}

AsyncServiceImpl

      /**
     * 使用InheritableThreadLocal获取主线程传递的数据
     * @return
     * @throws InterruptedException
     */
    @Async("asyncServiceExecutor")
    public CompletableFuture<String> executeValueAsync4() throws InterruptedException {
        log.info("start executeValueAsync");
        System.out.println("异步线程执行返回结果......+");
        log.info("end executeValueAsync");
        return CompletableFuture.completedFuture(UserInheritableUtils.getUserId());
    }

Test2Controller

       /**
     * 使用InheritableThreadLocal方式
     * @return
     * @throws InterruptedException
     * @throws ExecutionException
     */
    @GetMapping("/test4")
    public String test4(@RequestParam("userId") String userId) throws InterruptedException, ExecutionException {
        UserInheritableUtils.setUserId(userId);
        CompletableFuture<String> completableFuture = asyncService.executeValueAsync4();
        String s = completableFuture.get();
        return s;
    }

方案5.TransmittableThreadLocal

用户工具类 UserTransmittableUtils

/**
 *使用TransmittableThreadLocal存储线程之间共享的数据变量,如登录的用户信息
 */
public class UserTransmittableUtils {
    private static  final TransmittableThreadLocal<String> userLocal=new TransmittableThreadLocal<>();
    public static  String getUserId(){
        return userLocal.get();
    }
    public static void setUserId(String userId){
        userLocal.set(userId);
    }
    public static void clear(){
        userLocal.remove();
    }
}
}

AsyncServiceImpl

   /**
     * 使用TransmittableThreadLocal获取主线程传递的数据
     * @return
     * @throws InterruptedException
     */
    @Async("asyncServiceExecutor")
    public CompletableFuture<String> executeValueAsync6() throws InterruptedException {
        log.info("start executeValueAsync");
        System.out.println("异步线程执行返回结果......+");
        log.info("end executeValueAsync");
        return CompletableFuture.completedFuture(UserTransmittableUtils.getUserId());
    }

Test2Controller

   /**
     * 使用TransmittableThreadLocal方式
     * @return
     * @throws InterruptedException
     * @throws ExecutionException
     */
    @GetMapping("/test6")
    public String test6() throws InterruptedException, ExecutionException {
        UserTransmittableUtils.setUserId("123456");
        CompletableFuture<String> completableFuture = asyncService.executeValueAsync6();
        String s = completableFuture.get();
        return s;
    }

maven依赖

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

方案对比

方案1,方案2,方案3主要是借助TaskDecorator进行父子线程之间传递数据。其中MDC方案主要借鉴于MDC的日志跟踪的思想来实现,关于MDC相关的日志跟踪后续会学习分享

方案4和方案5使用InheritableThreadLocal和TransmittableThreadLocal来实现,其中TransmittableThreadLocal是阿里InheritableThreadLocal进行优化封装。

本人推荐使用方案5,哈哈。

简答说一下InheritableThreadLocal

public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1,1,1,
                TimeUnit.MINUTES,new ArrayBlockingQueue<>(1));
        ThreadLocal local = new InheritableThreadLocal();
        local.set(1);
        executor.execute(()->{
            System.out.println("打印1:"+local.get());
        });
        local.set(2);
        System.out.println("打印2:"+local.get());
        executor.execute(()->{
            System.out.println("打印3:"+local.get());
        });
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("打印4:"+local.get());
            }
        }).start();
    }

运行结果如下

打印2:2
打印1:1
打印3:1
打印4:2

分析: 分析打印3为什么是1,InheritableThreadLocal的继承性是在new Thread创建子线程时候在构造函数内把父线程内线程变量拷贝到子线程内部的。 为了不在创建新线程耗费资源,我们一般会用线程池,线程池的线程会复用,那么线程中的ThreadLocal便不对了,可能是旧的,因为线程是旧的。

总结

上面的的方案你学会了么

到此这篇关于SpringBoot父子线程数据传递的五种方案介绍的文章就介绍到这了,更多相关SpringBoot数据传递内容请搜索程序员之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持程序员之家!

相关文章

  • JavaCV实现读取视频信息及自动截取封面图详解

    JavaCV实现读取视频信息及自动截取封面图详解

    javacv可以帮助我们在java中很方便的使用OpenCV以及FFmpeg相关的功能接口。本文将利用Javacv实现在视频网站中常见的读取视频信息和自动获取封面图的功能,感兴趣的可以了解一下
    2022-06-06
  • Java实现Floyd算法求最短路径

    Java实现Floyd算法求最短路径

    这篇文章主要为大家详细介绍了Java实现Floyd算法求最短路径,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • SpringFactoriesLoader类作用详解

    SpringFactoriesLoader类作用详解

    SpringFactoriesLoader可以加载jar包下META-INF下的spring.factories,把相关接口的实现按照key,value的形式加载到内存,一个接口的多个实现可以按照","进行分割
    2022-10-10
  • 使用log4j2关闭debug日志

    使用log4j2关闭debug日志

    这篇文章主要介绍了使用log4j2关闭debug日志方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java动态代理语法Proxy类原理详解

    Java动态代理语法Proxy类原理详解

    这篇文章主要介绍了Java动态代理语法Proxy类原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • Maven?项目用Assembly打包可执行jar包的方法

    Maven?项目用Assembly打包可执行jar包的方法

    这篇文章主要介绍了Maven?项目用Assembly打包可执行jar包的方法,该方法只可打包非spring项目的可执行jar包,需要的朋友可以参考下
    2023-03-03
  • Springboot整合Shiro的代码实例

    Springboot整合Shiro的代码实例

    这篇文章主要介绍了Springboot整合Shiro的代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • java实现递归菜单树

    java实现递归菜单树

    这篇文章主要为大家详细介绍了java实现递归菜单树,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • java中常见的中文乱码总结

    java中常见的中文乱码总结

    本文主要介绍了java中常见的中文乱码以及解决方法,主要包括字节码文件读取时出现的乱码问题,本文通过实例代码给大家介绍的非常详细,具有很好的参考价值,感兴趣的朋友跟随小编一起看看吧
    2017-03-03
  • 详解ElasticSearch6.4.0集群搭建

    详解ElasticSearch6.4.0集群搭建

    这篇文章主要介绍了详解ElasticSearch6.4.0集群搭建,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09

最新评论

?


http://www.vxiaotou.com