SpringBoot中的異步執(zhí)行方法詳解
源碼跟蹤
簡單描述
在SpringBoot2.0.9之前需要手動自定義線程池(如下2.1), 然后指定線程池的名稱
SpringBoot2.0.9以及之前的版本,使用的線程池默認是SimpleAsyncTaskExcutor, 之后的版本使用的是ThreadpoolTaskExecutor
并且不需要手動的創(chuàng)建當(dāng)前線程池(但往往我們還是會手動指定,具體原因看源碼就可以自有判斷 )
SpringBoot會自動的掃描兩個文件下的配置信息:
所以如果我們寫的配置類想讓SpringBoot自動掃描到就可以放到兩個中的任意一個

我們項目中就是這樣使用的:在 spring.factories文件中指定一些配置類相對路徑,這樣配置類中的指定的Bean就可以放入到IOC容器中了

SpringBoot在org.springframework.boot.autoconfigure.AutoConfiguration.imports118行配置了TaskExecutionAutoConfiguration的位置,這樣SpringBoot就可以掃描到當(dāng)前配置類
![[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-sOUosu98-1685781636628)(C:\Users\57589\AppData\Roaming\Typora\typora-user-images\image-20230603155549533.png)]](http://img.jbzj.com/file_images/article/202310/202310190834363.jpg)
TaskExecutionAutoConfiguration
配置類信息如下
@ConditionalOnClass({ThreadPoolTaskExecutor.class}) // 代表如果容器中有這個類,就不在創(chuàng)建
@AutoConfiguration
@EnableConfigurationProperties({TaskExecutionProperties.class}) // 配置文件
public class TaskExecutionAutoConfiguration {
// 應(yīng)用程序任務(wù)執(zhí)行器任務(wù)名稱 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;
// ...... ......................省略
// 創(chuàng)建代碼
@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("默認核心線程數(shù):"+executor.getCorePoolSize());
System.out.println("默認最大線程數(shù):"+executor.getMaxPoolSize());
System.out.println("當(dāng)前活躍線程數(shù):"+executor.getActiveCount());
System.out.println("臨時線程空閑時間:"+executor.getKeepAliveSeconds());
System.out.println("隊列最大值:"+executor.getQueueCapacity());
System.out.println("隊列數(shù)量:"+executor.getQueueSize());
}
結(jié)果如下:
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor@7410c197
默認前綴:task-
默認核心線程數(shù):8
默認最大線程數(shù):2147483647
當(dāng)前活躍線程數(shù):0
臨時線程空閑時間:60
隊列最大值:2147483647
隊列數(shù)量:0
我們可以看到SpringBoot中默認配置的線程池的數(shù)量, 很不符合我們的實際要求, 而且還容易發(fā)生OOM(Out Of Memory)
所以我們一般是手動指定線程池中的信息
SpringBoot異步執(zhí)行方法
定義一個配置類
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
在想要異步執(zhí)行的方法上添加 @Async()注解
并指定ThreadPoolTaskExecutor 執(zhí)行器的名稱
@Override
@Async("generateExchangeCodeExecutor")
public void asyncGenerateCode(Coupon coupon) {
......
}
到此這篇關(guān)于SpringBoot中的異步執(zhí)行方法詳解的文章就介紹到這了,更多相關(guān)SpringBoot異步執(zhí)行內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis中有關(guān)int和Integer的使用方式
這篇文章主要介紹了MyBatis中有關(guān)int和Integer的使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
SpringBoot接收參數(shù)所有方式總結(jié)
這篇文章主要介紹了SpringBoot接收參數(shù)所有方式總結(jié),文中通過代碼示例和圖文結(jié)合的方式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-07-07
Mybatisplus創(chuàng)建Spring?Boot工程打包錯誤的解決方式
最近在實戰(zhàn)springboot遇到了一些坑,記錄一下,下面這篇文章主要給大家介紹了關(guān)于Mybatisplus創(chuàng)建Spring?Boot工程打包錯誤的解決方式,文中通過圖文介紹的介紹的非常詳細,需要的朋友可以參考下2023-03-03
java實現(xiàn)溫度單位轉(zhuǎn)換(攝氏度和華氏度)
在軟件開發(fā)中,溫度轉(zhuǎn)換是測量與控制系統(tǒng),氣象應(yīng)用,物聯(lián)網(wǎng)終端,科學(xué)計算等場景的基礎(chǔ)功能之一,所以本文將使用java實現(xiàn)溫度單位轉(zhuǎn)換功能,需要的可以了解下2025-07-07
Spring Security 中細化權(quán)限粒度的方法
這篇文章主要介紹了Spring Security 中細化權(quán)限粒度的方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
淺談SpringCloud的微服務(wù)架構(gòu)組件
這篇文章主要介紹了淺談SpringCloud的微服務(wù)架構(gòu)組件,Spring Cloud根據(jù)分布式服務(wù)協(xié)調(diào)治理的需求成立了許多子項目,每個項目通過特定的組件去實現(xiàn),需要的朋友可以參考下2023-04-04

