spring的ThreadPoolTaskExecutor装饰器传递调用线程信息给线程池中的线程

2023-09-18 15:49:37

概述

需求是想在线程池执行任务的时候,在开始前将调用线程的信息传到子线程中,在子线程完成后,再清除传入的数据。

下面使用了springThreadPoolTaskExecutor来实现这个需求.

ThreadPoolTaskExecutor

jdk中使用的是ThreadPoolExecutor,用于自定义线程池。
spring中则是对ThreadPoolExecutor又包了一层,加了一些参数进去ThreadPoolTaskExecutor,然后作为bean注入到springioc容器中.

通常在使用线程池的时候想把调用线程的一些信息传递给子线程(线程池中的线程),一般都是要自己写一个装饰器,然后把装饰器传递给线程池的execute方法。

不过spring中已经有现成的方法了,就在ThreadPoolTaskExecutor中,可以给定自定义的装饰器。

org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor#initializeExecutor
可以看到在初始化的时候会判断是否存在装饰器
在这里插入图片描述

ThreadPoolTaskExecutor使用装饰器传递调用线程信息

这样线程池中的线程在执行的时候都会经过装饰器处理,要注意的是在线程执行完成之后需要把信息清理,不然信息会串的

package org.xxx.common.core.executor.decorator;

import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.Map;


/**
 * 对spring的线程中的线程进行装饰
 */

public class ContextCopyingDecorator implements TaskDecorator {


    @Override
    public Runnable decorate(Runnable runnable) {
        try {

            //当前请求上下文
            RequestAttributes context = RequestContextHolder.currentRequestAttributes();

            //copy当前调用线程的 ThreadLocalMap 中保存的信息
            Map<String,String> previous = MDC.getCopyOfContextMap();
            return () -> {
                try {
                    //http request上下文塞到当前线程中
                    RequestContextHolder.setRequestAttributes(context);
                    //将调用线程的 ThreadLocalMap 塞到当前线程
                    MDC.setContextMap(previous);
                    runnable.run();
                } finally {
                    //clear
                    RequestContextHolder.resetRequestAttributes();
                    MDC.clear();
                }
            };
        } catch (IllegalStateException e) {
            return runnable;
        }
    }
}



//线程池配置


/**
 * 核心线程数 = cpu 核心数 + 1
 */
private final int core = Runtime.getRuntime().availableProcessors() + 1;

private ScheduledExecutorService scheduledExecutorService;

@Bean(name = "threadPoolTaskExecutor")
@ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true")
public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties threadPoolProperties) {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

    executor.setCorePoolSize(threadPoolProperties.getCoreSize());
    executor.setMaxPoolSize(threadPoolProperties.getMaxCoreSize());

    if(threadPoolProperties.getCoreSize() == 0) executor.setCorePoolSize(core);
    if(threadPoolProperties.getMaxCoreSize() == 0) executor.setMaxPoolSize(core * 2);

    //线程池队列大小
    executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());

    //线程空闲存活时间
    executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());

    //线程池拒绝时交由调用线程执行
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

    //装饰线程池中的线程
    executor.setTaskDecorator(new ContextCopyingDecorator());

    return executor;
}
更多推荐

多线程详解(上)

文章目录一、线程的概念1)线程是什么2)为甚要有线程(1)“并发编程”成为“刚需”(2)在并发编程中,线程比进程更轻量.3)线程和进程的区别二、Thread的使用1)线程的创建继承Thread类实现Runnable接口继承Thread类(使用匿名内部类)实现Runnable接口(使用匿名内部类)使用lambda2)Th

【探索C++】string类详解

(꒪ꇴ꒪),Hello我是祐言QAQ我的博客主页:C/C++语言,数据结构,Linux基础,ARM开发板,网络编程等领域UP🌍快上🚘,一起学习,让我们成为一个强大的攻城狮!送给自己和读者的一句鸡汤🤔:集中起来的意志可以击穿顽石!作者水平很有限,如果发现错误,请在评论区指正,感谢🙏在C++中,字符串处理是非常重要

Linux 文件、目录和用户权限管理指南

文章目录1.用户和组管理引言创建用户删除用户更改用户密码修改用户属性创建组删除组将用户添加到组将用户从组中移除2.文件和目录权限管理引言文件权限概述更改文件权限更改文件所有者和所属组更改目录权限列出文件和目录权限使用特殊权限文件和目录权限的案例分析继承父目录权限特殊权限的使用案例ACL(访问控制列表)umask注意事项

双网卡主机内网外网网关冲突问题探索(策略路由、网络命名空间)(内外网双网卡时,通常不需要在内网网卡上设置默认网关)

文章目录问题背景内外网双网卡时,通常不需要在内网网卡上设置默认网关1.网络冲突2.性能影响解决方法1.默认网关的作用2.只设置一个默认网关3.内网通信4.结论参考文章问题背景我们有一台windowsserver2012服务器,配置了双网卡,一个网卡配置外网,一个网卡配置内网,当我们将外网网络配置外网网关,内网网络配置内

uvm源码解读-sequence,sequencer,driver三者之间的握手关系1

1.startitem1.start_item();sequencer.wait_for_grant(prior);this.pre_do(1);需要指出,这里明确说明了wait_for_grant和send_request之间不能有任何延迟,所以在mid_do这个任务里千万不能有任何延迟。taskuvm_sequen

Spring AOP使用

SpringAOP是什么?AOP(面向切面编程):将那些与业务无关,却为业务模块所共同调用的逻辑(例如事务处理、日志管理、权限控制等)封装抽取成一个可重用的模块,这个模块被命名为“切面”(Aspect),便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性;在SpringAOP中,切面由切点(P

【2023集创赛】IEEE杯二等奖作品:高性能亳米波倍频程压控振荡器设计

本文为2023年第七届全国大学生集成电路创新创业大赛(“集创赛”)IEEE杯二等奖作品分享,参加极术社区的【有奖征集】分享你的2023集创赛作品,秀出作品风采,分享2023集创赛作品扩大影响力,更有丰富电子礼品等你来领!团队介绍参赛单位:南京邮电大学队伍名称:顺芯如意指导老师:谢祖帅,王子轩参赛队员:张文旭,汤金圣,秦

GE IS220PAICH2A 336A4940CSP11 控制脉冲模块

GEIS220PAICH2A336A4940CSP11控制脉冲模块是一种用于工业自动化和控制系统的模块,通常用于监测和生成脉冲信号,以控制各种设备和过程。以下是可能与该控制脉冲模块相关的一些产品功能:脉冲生成:GEIS220PAICH2A336A4940CSP11控制脉冲模块通常具有脉冲生成功能,可以生成具有特定频率、

基于STM32的简化版智能手表

一、前言本文的OLED多级菜单UI为一个综合性的STM32小项目,使用多传感器与OLED显示屏实现智能终端的效果。项目中的多级菜单UI使用了较为常见的结构体索引法去实现功能与功能之间的来回切换,搭配DHT11,RTC,LED,KEY等器件实现高度智能化一体化操作。后期自己打板设计结构,可以衍生为智能手表等小玩意。目前,

java学习--day10 (继承)

文章目录day9作业今天的内容1.继承1.1.生活中的继承1.2.Java中继承1.3关于父类子类的内存分析1.4重写【重点】1.5重载【overload】day9作业1.构造代码块和构造方法的区别{代码块}public类名(){}都是实例化一个对象的时候执行的只不过构造代码块先于构造方法执行的2.局部变量和成员变量区

软件测试/测试开发丨ChatGPT在测试计划中的应用策略

点此获取更多相关资料简介测试计划是指描述了要进行的测试活动的范围、方法、资源和进度的文档。它主要包括测试项、被测特性、测试任务和风险控制等。所以在使用ChatGPT输出结果之前,我们需要先将文档的内容框架梳理好,以及将内容范围划定好,必要的时候,可以添加对应的角色。实践演练提示词:如果我是一个测试经理,现在需要输出一个

热文推荐