SpringBoot中的异步执行方法详解

 更新时间:2023年10月19日 08:33:41   作者:yfs1024  
这篇文章主要介绍了SpringBoot中的异步执行方法详解,ThreadpoolTaskExecutor不需要手动的创建当前线程池,但往往我们还是会手动指定,具体原因看源码就可以自有判断,需要的朋友可以参考下

源码跟踪

简单描述

在SpringBoot2.0.9之前需要手动自定义线程池(如下2.1), 然后指定线程池的名称

SpringBoot2.0.9以及之前的版本,使用的线程池默认是SimpleAsyncTaskExcutor, 之后的版本使用的是ThreadpoolTaskExecutor

并且不需要手动的创建当前线程池(但往往我们还是会手动指定,具体原因看源码就可以自有判断 )

SpringBoot会自动的扫描两个文件下的配置信息:

所以如果我们写的配置类想让SpringBoot自动扫描到就可以放到两个中的任意一个

我们项目中就是这样使用的:在 spring.factories文件中指定一些配置类相对路径,这样配置类中的指定的Bean就可以放入到IOC容器中了

SpringBoot在org.springframework.boot.autoconfigure.AutoConfiguration.imports118行配置了TaskExecutionAutoConfiguration的位置,这样SpringBoot就可以扫描到当前配置类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sOUosu98-1685781636628)(C:\Users\57589\AppData\Roaming\Typora\typora-user-images\image-20230603155549533.png)]

TaskExecutionAutoConfiguration

配置类信息如下

@ConditionalOnClass({ThreadPoolTaskExecutor.class})   // 代表如果容器中有这个类,就不在创建
@AutoConfiguration
@EnableConfigurationProperties({TaskExecutionProperties.class})  // 配置文件
public class TaskExecutionAutoConfiguration {
    // 应用程序任务执行器任务名称 applicationTaskExecutor
    public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";

    public TaskExecutionAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public TaskExecutorBuilder taskExecutorBuilder(TaskExecutionProperties properties, ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers, ObjectProvider<TaskDecorator> taskDecorator) {
        Pool pool = properties.getPool();
        TaskExecutorBuilder builder = new TaskExecutorBuilder();
        builder = builder.queueCapacity(pool.getQueueCapacity());
        builder = builder.corePoolSize(pool.getCoreSize());
        builder = builder.maxPoolSize(pool.getMaxSize());
        builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
        builder = builder.keepAlive(pool.getKeepAlive());
        Shutdown shutdown = properties.getShutdown();
        builder = builder.awaitTermination(shutdown.isAwaitTermination());
        builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());
        builder = builder.threadNamePrefix(properties.getThreadNamePrefix());
        Stream var10001 = taskExecutorCustomizers.orderedStream();
        var10001.getClass();
        builder = builder.customizers(var10001::iterator);
        builder = builder.taskDecorator((TaskDecorator)taskDecorator.getIfUnique());
        return builder;
    }

    @Lazy
    @Bean(
        name = {"applicationTaskExecutor", "taskExecutor"}
    )
    @ConditionalOnMissingBean({Executor.class})
    public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
        return builder.build();
    }

TaskExecutionProperties

配置文件中

定义了线程名 task -

ThreadPoolTaskExecutor

public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
		implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {

	private final Object poolSizeMonitor = new Object();

	private int corePoolSize = 1;

	private int maxPoolSize = Integer.MAX_VALUE;

	private int keepAliveSeconds = 60;

	private int queueCapacity = Integer.MAX_VALUE;

	private boolean allowCoreThreadTimeOut = false;

	private boolean prestartAllCoreThreads = false;
	
	//       ......  ......................省略
	// 创建代码
	@Override
	protected ExecutorService initializeExecutor(
			ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {

		BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);

		ThreadPoolExecutor executor;
		if (this.taskDecorator != null) {
            // 还是 new ThreadPoolExecutor
			executor = new ThreadPoolExecutor(
					this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
					queue, threadFactory, rejectedExecutionHandler) {
				@Override
				public void execute(Runnable command) {
					Runnable decorated = taskDecorator.decorate(command);
					if (decorated != command) {
						decoratedTaskMap.put(decorated, command);
					}
					super.execute(decorated);
				}
			};
		}
		else {
			executor = new ThreadPoolExecutor(
					this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
					queue, threadFactory, rejectedExecutionHandler);
		}

		if (this.allowCoreThreadTimeOut) {
			executor.allowCoreThreadTimeOut(true);
		}
		if (this.prestartAllCoreThreads) {
			executor.prestartAllCoreThreads();
		}

		this.threadPoolExecutor = executor;
		return executor;
	}

测试代码:

// 注入
@Autowired
private ThreadPoolTaskExecutor executor;

@Test
public void testThreadPool(){
    System.out.println(executor);
    System.out.println("默认前缀:"+executor.getThreadNamePrefix());
    System.out.println("默认核心线程数:"+executor.getCorePoolSize());
    System.out.println("默认最大线程数:"+executor.getMaxPoolSize());
    System.out.println("当前活跃线程数:"+executor.getActiveCount());
    System.out.println("临时线程空闲时间:"+executor.getKeepAliveSeconds());
    System.out.println("队列最大值:"+executor.getQueueCapacity());
    System.out.println("队列数量:"+executor.getQueueSize());
}

结果如下:

org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor@7410c197
默认前缀:task-
默认核心线程数:8
默认最大线程数:2147483647
当前活跃线程数:0
临时线程空闲时间:60
队列最大值:2147483647
队列数量:0

我们可以看到SpringBoot中默认配置的线程池的数量, 很不符合我们的实际要求, 而且还容易发生OOM(Out Of Memory)

所以我们一般是手动指定线程池中的信息

SpringBoot异步执行方法

定义一个配置类

SpringBoot底层对手动注入的Bean采用的名称如果不在@Bean注解后面指定默认采用的是方法名

即: 这里的 generateExchangeCodeExecutor

@Slf4j
@Configuration
public class PromotionConfig {

    @Bean
    public Executor generateExchangeCodeExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 1.核心线程池大小
        executor.setCorePoolSize(2);
        // 2.最大线程池大小
        executor.setMaxPoolSize(5);
        // 3.队列大小
        executor.setQueueCapacity(200);
        // 4.线程名称
        executor.setThreadNamePrefix("exchange-code-handler-");
        // 5.拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

在启动类上添加注解

@EnableAsync

在想要异步执行的方法上添加 @Async()注解

并指定ThreadPoolTaskExecutor 执行器的名称

    @Override
    @Async("generateExchangeCodeExecutor")
    public void asyncGenerateCode(Coupon coupon) {
    		......
    }

到此这篇关于SpringBoot中的异步执行方法详解的文章就介绍到这了,更多相关SpringBoot异步执行内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MyBatis中有关int和Integer的使用方式

    MyBatis中有关int和Integer的使用方式

    这篇文章主要介绍了MyBatis中有关int和Integer的使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • SpringBoot接收参数所有方式总结

    SpringBoot接收参数所有方式总结

    这篇文章主要介绍了SpringBoot接收参数所有方式总结,文中通过代码示例和图文结合的方式给大家介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-07-07
  • Mybatisplus创建Spring Boot工程打包错误的解决方式

    Mybatisplus创建Spring Boot工程打包错误的解决方式

    最近在实战springboot遇到了一些坑,记录一下,下面这篇文章主要给大家介绍了关于Mybatisplus创建Spring Boot工程打包错误的解决方式,文中通过图文介绍的介绍的非常详细,需要的朋友可以参考下
    2023-03-03
  • Go Java算法之解码方法示例详解

    Go Java算法之解码方法示例详解

    这篇文章主要为大家介绍了Go Java算法之解码方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • java调用短信猫发短信示例

    java调用短信猫发短信示例

    这篇文章主要介绍了java调用短信猫发短信示例,需要的朋友可以参考下
    2014-04-04
  • 教你在SpringBoot中管理多环境配置文件

    教你在SpringBoot中管理多环境配置文件

    在项目开发中,一般会分为开发环境,测试环境和生产环境.如果总是在一个文件中修修改改,难免会出现错误,尤其是在交接的过程中,一旦出现问题,轻则丢失绩效,重则需要删库跑路.本文则带大家详细了解怎么管理多环境配置文件,需要的朋友可以参考下
    2021-05-05
  • IDEA新建的Moudle失效显示为灰色的完美解决方案

    IDEA新建的Moudle失效显示为灰色的完美解决方案

    这篇文章主要介绍了IDEA新建的Moudle失效显示为灰色,本文通过图文并茂的形式给大家分享完美解决方案,需要的朋友可以参考下
    2023-09-09
  • java实现温度单位转换(摄氏度和华氏度)

    java实现温度单位转换(摄氏度和华氏度)

    在软件开发中,温度转换是测量与控制系统,气象应用,物联网终端,科学计算等场景的基础功能之一,所以本文将使用java实现温度单位转换功能,需要的可以了解下
    2025-07-07
  • Spring Security 中细化权限粒度的方法

    Spring Security 中细化权限粒度的方法

    这篇文章主要介绍了Spring Security 中细化权限粒度的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • 浅谈SpringCloud的微服务架构组件

    浅谈SpringCloud的微服务架构组件

    这篇文章主要介绍了浅谈SpringCloud的微服务架构组件,Spring Cloud根据分布式服务协调治理的需求成立了许多子项目,每个项目通过特定的组件去实现,需要的朋友可以参考下
    2023-04-04

最新评论