Spring异步
约 1208 字大约 4 分钟
2025-07-23
Spring异步相关代码位于 `spring-context` jar包中。异步是Spring相对核心的功能。使用之前需要先开启。
官方文档:https://docs.spring.io/spring-framework/reference/integration/scheduling.html
一、开启异步
创建一个配置类,或者在现有配置类上通过添加 `@EnableAsync` 来开启异步功能。
@Configuration
@EnableAsync
public class AsyncConfig {
}二、常用异步方法
使用时,通过在方法上添加 `@Async` 注解来使用,方法会在异步线程中执行。
@Async
public void asyncMethod() {
// 异步执行的代码
}如果需要关注异步方法的执行结果,可以返回 `Future` 对象。
@Async
public Future<String> asyncMethodWithReturn() {
// 异步执行的代码
return "异步执行的结果";
}三、定制化线程池
使用 `@Async` 注解时,默认走的是Spring自带的线程池,默认线程可能会有些限制,大小和容量限制。
如果我们系统中的任务如果要分组的话,分组之间相互不影响的话,那么就需要考虑指定线程池的方式了。
1、自定义线程池
@Configuration
public class AsyncConfig {
@Bean(name = "customExecuterA")
public Executor getAsyncExecutorA() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数
executor.setCorePoolSize(10);
//最大线程数
executor.setMaxPoolSize(20);
//队列容量
executor.setQueueCapacity(100);
//线程名称前缀
executor.setThreadNamePrefix("CustomeThreadPoolA-");
//线程空闲时间
executor.setKeepAliveSeconds(60);
return executor;
}
@Bean(name = "customExecuterB")
public Executor getAsyncExecutorB() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数
executor.setCorePoolSize(10);
//最大线程数
executor.setMaxPoolSize(20);
//队列容量
executor.setQueueCapacity(100);
//线程名称前缀
executor.setThreadNamePrefix("CustomeThreadPoolB-");
//线程空闲时间
executor.setKeepAliveSeconds(60);
return executor;
}
}2、使用自定义线程池
在使用 `@Async` 注解时,可以传入线程池名称名称。`@Async("customExecuter")` 下面是一个线程池定义和使用的例子:
// 异步方法A使用线程池A
@Async("customExecuterA")
public void asyncMethodA() {
// 异步执行的代码
}
// 异步方法B使用线程池B
@Async("customExecuterB")
public void asyncMethodB() {
// 异步执行的代码
}3、注意Spring注解失效场景
对于使用 `@Async` 注解的方法,有一些注意事项。
- 异步方法必须是public方法,原因是Spring中的异步功能是通过代理实现的,私有方法不能被代理。
- 异步方法最好是外部调用的方法,不能是内部调用的方法。如果确定要内部调用,需要使用
自注册方式。 - 异步方法最好是void类型的方法,不能是有返回值的方法。如果有返回值,需要通过
Feature方式实现。
四、定制化异步任务
很多时候,我们为了能够提高接口速度,会将任务拆分异步,如果使用上面的 `Async` 注解方式,会非常的繁琐,还不好处理任务结果。
所以,我们需要使用Java原始异步任务来实现并发任务,这时候就需要将原始的线程池与Spring结合使用。
1、自定义线程池
基于Spring的Bean方式自定义线程池,可以做到全局通用使用。
@EnableAsync
@Configuration
public class AsyncConfig {
@Bean
public ExecutorService createExecutorService() {
return new ThreadPoolExecutor(
10, // 初始化大小
100, // 最大线程数
6000, // 线程空闲时间
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(2000),
new BasicThreadFactory.Builder().namingPattern("default-pool-thread-%d").build()
);
}
}通过配置方式自定义线程池
@EnableAsync
@Configuration
@EnableConfigurationProperties(ThreadPoolProperties.class)
public class AsyncConfig {
private final ThreadPoolProperties threadPoolProperties;
public AsyncConfig(ThreadPoolProperties threadPoolProperties) {
this.threadPoolProperties = threadPoolProperties;
}
@Bean
public ExecutorService createExecutorService() {
return new ThreadPoolExecutor(
threadPoolProperties.getMinSize(),
threadPoolProperties.getMaxSize(),
threadPoolProperties.getKeepAliveTime(),
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(threadPoolProperties.getWaitSize()),
new BasicThreadFactory.Builder().namingPattern("default-pool-thread-%d").build()
);
}
}线程池配置类:
@Setter
@Getter
@ConfigurationProperties(prefix = "thread.pool")
public class ThreadPoolProperties {
/**
* 最小
*/
private Integer minSize = 10;
/**
* 最大
*/
private Integer maxSize = 100;
/**
* 等待队列大小
*/
private Integer waitSize = 500;
/**
* 存活时间。单位毫秒
*/
private Long keepAliveTime = 30000L;
}配置文件:
thread:
pool:
min-size: 10
max-size: 100
keep-alive-time: 60000
wait-size: 20002、使用自定义线程池
在代码中通过 `@Autowired` 注解注入线程池服务后,然后使用线程池执行任务。
CompletionService<Integer> completionService = new ExecutorCompletionService<>(executorService);
// 提交任务
completionService.submit(() -> {
// 异步执行的代码
return 1;
});
// 等到任务:这里的10 代表提交的任务数量
for (int i = 0; i < 10; i++) {
Future<Integer> future = completionService.take();
Integer result = future.get();
System.out.println(result);
}这里之所以使用 CompletionService 是因为它可以按照任务完成的顺序获取任务的结果,而不是按照任务提交的顺序获取任务的结果。
另外,使用 CompletionService 服务可以只获取到由他所提交的这些任务的结果。和其他的并行使用 executorService 是不会冲突的。
注意:
- 这里的
CompletionService服务是线程安全的,所以可以在多个线程中使用。 - 这里的
CompletionService服务是阻塞的,所以在获取任务结果时,需要注意线程的阻塞问题。 - 这里的
CompletionService服务是异步的,所以在提交任务时,不需要等待任务完成。
