首页 > 其他分享 >Spring异步线程池-TaskDecorator传递线程上下文

Spring异步线程池-TaskDecorator传递线程上下文

时间:2023-10-25 16:12:50浏览次数:36  
标签:Spring TaskDecorator 复用 InheritableThreadLocal TransmittableThreadLocal executor

TaskDecorator:

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

  从主线程拷贝数据到子线程,具体数据实际上是封装到threadlocal里面。

实现方式:

  定义一个TaskDecorator,在线程池中设置使用这个TaskDecorator。

  注意线程池中有的线程是一直存在一直被复用的,所以线程执行完成后需要在TaskDecorator的finally方法中移除传递的上下文对象,否则就存在内存泄漏问题。

  定义TaskDecorator实例:
  继承TaskDecorator接口创建ContextCopyingDecorator 实现类,重写decorate方法,设置需要传递的上下文和变量值。注意finally代码块。

  public class ContextCopyingDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
      try {
        RequestAttributes context = RequestContextHolder.currentRequestAttributes(); //1
        Map<String,String> previous = MDC.getCopyOfContextMap(); //2
        SecurityContext securityContext = SecurityContextHolder.getContext(); //3
        return () -> {
          try {
            RequestContextHolder.setRequestAttributes(context); //1
            MDC.setContextMap(previous); //2
            SecurityContextHolder.setContext(securityContext); //3
            runnable.run();
          } finally {
            RequestContextHolder.resetRequestAttributes(); // 1
            MDC.clear(); // 2
            SecurityContextHolder.clearContext(); // 3
          }
        };
      } catch (IllegalStateException e) {
        return runnable;
      }
    }
  }

  这里使用了slf4j-api的jar包里面的一个MDC的类。通过查看源码发现,该类实际上是封装了threadlocal。子线程runnable执行前,通过静态方法getCopyOfContextMap获取主线程里面的数据,在子线程里面通过setContextMap方法设置到子线程的threadlocal里面。这样子线程就获取到主线程的数据了。

线程池使用TaskDecorator:

  @Bean("taskExecutor")
  public TaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(corePoolSize);
    executor.setMaxPoolSize(maxPoolSize);
    executor.setQueueCapacity(queueCapacity);
    executor.setThreadNamePrefix("MyExecutor-");

    // for passing in request scope context
    executor.setTaskDecorator(new ContextCopyingDecorator());
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.setWaitForTasksToCompleteOnShutdown(true);
    executor.initialize();
    return executor;
  }

 

涉及知识点

ThreadLocal,InheritableThreadLocal,TaskDecorator,RequestContextHolder,TransmittableThreadLocal(通过继承InheritableThreadLocal实现)

测试 ThreadLocal,InheritableThreadLocal,TransmittableThreadLocal的区别和使用

1.父线程使用ThreadLocal,子线程创建时不会拥有父类的threadLocal信息
2.父线程使用InheritableThreadLocal,子线程创建时,默认init方法会拿到父类的InheritableThreadLocal信息,这种在线程池/线程复用的情况下,由于init方法只会在初始化时获取父线程的数据,复用的时候也没法再从父线程那里新的InheritableThreadLocal的数据,此种情况下继续使用,很容易出bug(InheritableThreadLocal适用于非线程池和复用线程,单独创建销毁子线程执行的情况)
3.父线程使用TransmittableThreadLocal,子线程创建时拥有父类的TransmittableThreadLocal信息,在线程池/线程复用的情况下不会出现读取到脏数据的情况

总结

  • 在异步线程池的情况下,通过ThreadLocal+TaskDecorator一般即可解决遇到的透传问题;
  • TransmittableThreadLocal,其原理也是对Runnable,Callable,进行装饰;

标签:Spring,TaskDecorator,复用,InheritableThreadLocal,TransmittableThreadLocal,executor
From: https://www.cnblogs.com/m-zone/p/17787458.html

相关文章

  • 多线程指南:探究多线程在Node.js中的广泛应用
    前言最初,JavaScript是用于设计执行简单的web任务的,比如表单验证。直到2009年,Node.js的创建者RyanDahl让开发人员认识到了通过JavaScript进行后端开发已成为可能,在后端开发中,用到最多的就是多线程以及线程之间的同步功能,今天小编就为大家介绍一下如何使用Node.js实现多线程的应......
  • springboot 06 idea提交到Gitee
         --->commit   首次要填Gitee的账号密码  ......
  • SpringBoot内容协商(Content Negotiation)二 —— 自定义消息转换器(MessageConverter)
    SpringBoot内置的消息转换器SpringBoot没有处理返回yaml格式的数据,这里需要手动添加处理这种返回格式的支持。导入依赖<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-yaml</artifactId></dependency>添加配......
  • 【Springboot文件上传】前后端双开,大文件秒传、断点续传的解决方案和优雅实现
    思路和解决方案探讨秒传这里指的“秒传”,是指:当用户选择上传一个文件时,服务端检测该文件之前是否已经被上传过,如果服务器已经存有该文件(完全一样),就立马返回前端“文件已上传成功”。前端随即将进度条更新至100%。这样给用户的感觉就是“秒传”的感觉。对于每一个上传到服务......
  • SpringBoot自动配置原理解析 | 京东物流技术团队
    1:什么是SpringBoot自动配置首先介绍一下什么是SpringBoot,SpringBoost是基于Spring框架开发出来的功能更强大的Java程序开发框架,其最主要的特点是:能使程序开发者快速搭建一套开发环境。SpringBoot能将主流的开发框架(例如SpringMVC,Dubbo,Mybatis,Redis等),做到像Maven导入Jar包一样......
  • springboot解决跨域
    新建config包在建文件复制进去即可importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.cors.CorsConfiguration;importorg.springframework.web.cors.UrlBasedCorsConfigura......
  • 进程,线程,线程生命周期,原生线程,线程调度,Thread,ThreadPool,Task,Parallel,线程安全容器
    1.进程;程序在服务器上运行时,占用的计算机资源合集,就是进程2.线程:是程序能够独立运行的最小单位,共享进程的资源;3.线程的生命周期:3.1新建,启动,可运行,正在运行,new,start,runnable,running,dead,blocked阻塞4.原生线程:由操作系统负责创建、运行、切换、终止的线程就是原生线程5.线程......
  • java spring,springmvc,spring boot,spring data,RESTful api设计风格,HTTP协议的四种传
    一.基本技术1.Springmvc=手动接电线(配置xml),提供了一种友好的方式来开发Web应用程序。通过使用诸如DispatcherServlet,ModelAndView和ViewResolver,可以轻松开发Web应用程序。2.SpringBoot=标准插座(第3方开源类库想接入,就按照标准做一个starter的适配),实现了免xml配置和提供......
  • springboot 整合 gridfs 、webUploader实现大文件分块上传、断点续传、秒传
    主要的pom.xml:<dependency>      <groupId>mysql</groupId>      <artifactId>mysql-connector-java</artifactId>    </dependency><!--mongodb-->    <dependency>      <groupId>org.spri......
  • Qt - 多线程之QtConcurrent::run()
    QT多线程之QtConcurrent::run()QT有几种可以实现多线程编程的方式,其中最方便使用,最便携的一定是QtConcurrent::run()了,这是一个模板函数,有很多的重载原型。//在新的线程中调用普通函数template<typenameT>QFuture<T>QtConcurrent::run(Functionfunction,...)//使用线......