SpringBoot中@Async的实现方式探索

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
云原生内存数据库 Tair,内存型 2GB
简介: 本文探讨了一段重写了默认`@Async`线程处理的Scala代码,并对其目的与必要性提出了疑问。原代码通过自定义`AsyncExecutorConfig`类,实现了`AsyncConfigurerSupport`接口,进而配置了一个`ThreadPoolExecutor`作为异步任务的执行器。文章随后分析了为何要替换默认实现,并对Spring框架中`@Async`注解的默认行为进行了深入研究。

背景

最近看代码时看到小伙伴提交了这样一段代码

scala

代码解读

复制代码

public class AsyncExecutorConfig extends AsyncConfigurerSupport {
    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolExecutor(
                10,
                300,
                30,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(2000),
                ThreadFactoryBuilder.create().setNamePrefix("bid-async-").build(),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }

代码的目的是重新实现@Aysnc的默认线程处理,于是有一个疑问,为什么要进行重写,使用默认实现不可以吗?@Async默认的实现方式是什么?原理又是什么呢?

带着这个疑问进行了相关探索

探索

从修改默认实现上看,默认实现一定也是一个线程池,于是查看他的默认线程池是什么?首先我看到的默认实现是会使用SimpleAsyncTaskExecutor线程池,那看SimpleAsyncTaskExecutor线程池的实现方式,他的方式是有一个任务就去创建一个线程,而且创建的线程不会复用且不会销毁,当任务过多时,会出现cpu过高的情况,基于此,原有的实现是一定存在问题的。

随着探索的加深,发现存在另一个答案,就是他的实现也是ThreadPoolExecutor,在springboot2.1.0之前默认是实现是SimpleAsyncTaskExecutor,2.1.0之后默认实现变更为ThreadPoolExecutor,我们看看变更之后是怎么实现默认线程池的

kotlin

代码解读

复制代码

@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@Configuration
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration {

	/**
	 * Bean name of the application {@link TaskExecutor}.
	 */
	public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";

	private final TaskExecutionProperties properties;

	private final ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers;

	private final ObjectProvider<TaskDecorator> taskDecorator;

	public TaskExecutionAutoConfiguration(TaskExecutionProperties properties,
			ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers,
			ObjectProvider<TaskDecorator> taskDecorator) {
		this.properties = properties;
		this.taskExecutorCustomizers = taskExecutorCustomizers;
		this.taskDecorator = taskDecorator;
	}
        
        
        

kotlin

代码解读

复制代码

protected ExecutorService initializeExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
        BlockingQueue<Runnable> queue = this.createQueue(this.queueCapacity);
        ThreadPoolExecutor executor;
        if (this.taskDecorator != null) {
            executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler) {
                public void execute(Runnable command) {
                    Runnable decorated = ThreadPoolTaskExecutor.this.taskDecorator.decorate(command);
                    if (decorated != command) {
                        ThreadPoolTaskExecutor.this.decoratedTaskMap.put(decorated, command);
                    }

                    super.execute(decorated);
                }
            };
        } else {
            executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler);
        }

默认线程池的定义了核心线程数,最大线程数,队列以及线程存活时间,他们的参数分别为

  • 核心线程数:8
  • 最大线程数:int的最大值
  • 队列:阻塞队列且最大值为int的最大值
  • 线程存活时间:60s
  • 拒绝策略:AbortPolicy

对比上一个默认实现,一个很大的进步是线程是可以重复利用的,这样就大大减少了资源的使用,但是还是存在一个较大的风险,就是队列长度过长,当任务过多时会将大量待执行的任务放到队列里面,导致程序处理不过来,最大线程数其实没有利用起来,所以保险起见,还是要自己手动实现一个线程池,防止出现相关问题

结语

线程池可以很好的提高功能的效率,但是也要考虑使用过程中出现的问题,比如多线程写,又比如使用不当造成的系统性能问题,基于此,是很有必要了解他们的执行方式和原理


转载来源:https://juejin.cn/post/7370963861644771337

相关文章
|
11月前
|
Java API Spring
【Springboot】springboot 多线程@EnableAsync和@Async
【Springboot】springboot 多线程@EnableAsync和@Async
79 0
|
4月前
|
Java 测试技术 Spring
Spring Boot使用@Async实现异步调用
Spring Boot使用@Async实现异步调用
42 0
|
14天前
|
Java 开发者 Spring
【SpringBoot 异步魔法】@Async 注解:揭秘 SpringBoot 中异步方法的终极奥秘!
【8月更文挑战第25天】异步编程对于提升软件应用的性能至关重要,尤其是在高并发环境下。Spring Boot 通过 `@Async` 注解简化了异步方法的实现。本文详细介绍了 `@Async` 的基本用法及配置步骤,并提供了示例代码展示如何在 Spring Boot 项目中创建与管理异步任务,包括自定义线程池、使用 `CompletableFuture` 处理结果及异常情况,帮助开发者更好地理解和运用这一关键特性。
71 1
|
4月前
|
Java Spring
SpringBoot @Async 注解
【1月更文挑战第1天】SpringBoot @Async 注解
|
4月前
|
Java Spring
SpringBoot - @Async异步任务与线程池
SpringBoot - @Async异步任务与线程池
166 1
|
4月前
|
Java Spring
使用spring boot的@Async实现异步调用和线程池复用
使用spring boot的@Async实现异步调用和线程池复用
|
前端开发 Java 测试技术
SpringBoot-27- @Async实现异步调用 什么是异步调用
异步调用是相对于同步调用的,同步调用是按照顺序进行执行任务,只有上一个任务执行完成下一个任务才能执行,异步调用是指在按照顺序执行任务的过程中不需要等待任务结果的出现,就可以顺序执行下一个任务。
79 0
|
11月前
|
Java 测试技术 Spring
Spring Boot + @Async =?
Spring Boot + @Async =?
42 0
|
Java Spring
使用spring boot的@Async实现异步调用和线程池复用
使用spring boot的@Async实现异步调用和线程池复用
826 0
使用spring boot的@Async实现异步调用和线程池复用
|
12月前
|
安全 Java UED
SpringBoot 如何使用 @Async 注解处理异步事件
SpringBoot 如何使用 @Async 注解处理异步事件