Spring中实现异步调用的方式有哪些?

开发 前端
只要在异步方法上添加@Bean注解,不需要手动调用线程池的initialize()方法,在Bean在初始化之后会自动调用。需要注意的是,在同级类中直接调用异步方法无法实现异步。

一位3年工作经验的小伙伴被问到这样一道面试题,说Spring中实现异步调用的方式有哪些?

今天,我给大家分享一下我的理解。

在Spring中,实现异步调用主要有三种方式,分别是注解方式、内置线程池方式和自定义线程池方式。

1、注解方式

可以在配置类和方法上加特定注解。首先,在配置类加上@EnableAsync来启用异步注解,

如代码所示:

@EnableAsync//启用异步支持
@Configuration
public class AppConfig {
}

然后,使用@Async注解标记需要异步执行的方法,

如代码所示:

@Async
void doSomething() {
// this will be run asynchronously
}

@Async
void doSomething(String s) {
// this will be run asynchronously
}

@Async
Future<String> returnSomething(int i) {
// this will be run asynchronously
}

使用@Async标记的异步方法可以带参数,也可以带有返回值。返回值类型必须是java.util.concurrent.Future或其子类,可以是以下3种类型:

1)由Java原生API提供的Future。

2)由Spring提供的ListenableFuture后者AsyncResult。

3)Java 8提供的CompletableFuture。

需要说明的是,@Async默认会使用SimpleAsyncTaskExecutor来执行,而这个线程池不会复用线程。所以,通常要使用异步处理,我们都会自定义线程池。

2、内置线程池方式

可以使用Spring内置的线程池来实现异步调用,比如ThreadPoolTaskExecutor 和SimpleAsyncTaskExecutor。Spring提供了许多TaskExecutor的内置实现。下面简单介绍5种内置的线程池。

1)SimpleAsyncTaskExecutor:它不会复用线程,每次调用都是启动一个新线程。

2)ConcurrentTaskExecutor:它是Java API中Executor实例的适配器。

3)ThreadPoolTaskExecutor:这个线程池是最常用的。它公开了用于配置的bean属性,并将它包装在TaskExecutor中。

4)WorkManagerTaskExecutor:它基于CommonJ WorkManager来实现的,并且是在Spring上下文中的WebLogic或WebSphere中设置CommonJ线程池的工具类。

5)DefaultManagedTaskExecutor:主要用于支持JSR-236兼容的运行时环境,它是使用JNDI获得ManagedExecutorService,作为CommonJ WorkManager的替代方案。

通常情况下,ThreadPoolTaskExecuto最为常用,只要当ThreadPoolTaskExecutor不能满足需求时,可以使用ConcurrentTaskExecutor。如果在代码中声明了多个线程池,Spring会默认按照以下搜索顺序来调用线程池:

第一步,检查上下文中的唯一TaskExecutor Bean。

第二步,检查名为“ taskExecutor”的Executor Bean。

第三步,以上都无法无法处理,就会使用SimpleAsyncTaskExecutor来执行。

3、自定义线程池方式

可以通过实现AsyncConfigurer接口或者直接继承AsyncConfigurerSupport类来自定义线程池。但是非完全托管的Bean和完全托管的Bean实现方式有点小差异。

首先,来看非完全托管的Spring Bean,实现方式如代码所示:

@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {

@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();//手动初始化
return executor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new MyAsyncUncaughtExceptionHandler();
}
}

在这段代码中,ThreadPoolTaskExecutor不是完全托管的Spring bean。

然后,来看完全托管的Spring Bean,实现方式如代码所示:

@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {

@Bean
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyExecutor-");
//executor.initialize();//不用手动调用
return executor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new MyAsyncUncaughtExceptionHandler();
}
}

只要在异步方法上添加@Bean注解,不需要手动调用线程池的initialize()方法,在Bean在初始化之后会自动调用。需要注意的是,在同级类中直接调用异步方法无法实现异步。

以上就是我对Spring实现异步调用的理解。

责任编辑:武晓燕 来源: Tom弹架构
相关推荐

2022-09-28 14:54:07

Spring注解方式线程池

2010-03-01 14:01:50

WCF服务异步调用

2009-07-01 14:37:14

JavaScript异

2009-07-01 14:23:46

JavaScript异

2021-03-29 09:26:44

SpringBoot异步调用@Async

2009-10-20 16:48:30

C#委托

2009-08-21 11:24:16

C#异步调用

2020-01-02 16:30:02

Spring BootJava异步请求

2023-08-23 13:24:00

异步编程方法

2021-03-19 10:14:28

SpringBoot项目异步调用

2018-06-21 14:46:03

Spring Boot异步调用

2010-02-25 09:13:34

WCF异步调用

2009-12-21 14:10:26

WCF异步调用

2009-11-09 10:50:30

WCF异步调用

2009-07-01 14:31:01

JavaScript异

2009-07-01 13:58:00

JavaScript异

2009-11-06 15:54:15

WCF异步调用

2009-08-21 11:02:55

C#异步调用

2022-07-01 08:14:28

Dubbo异步代码

2010-02-22 13:28:05

WCF异步调用
点赞
收藏

51CTO技术栈公众号