Springboot應(yīng)用中線程池配置詳細教程(最新2021版)
前言:日常開發(fā)中我們常用ThreadPoolExecutor提供的線程池服務(wù)幫我們管理線程,在Springboot中更是提供了@Async注解來簡化業(yè)務(wù)邏輯提交到線程池中執(zhí)行的過程。由于Springboot中默認設(shè)置的corePoolSize=1和queyeCapacity=Integer.MAX_VALUE,相當(dāng)于采用單線程處理所有任務(wù),這就與多線程的目的背道而馳,所以這就要求我們在使用@Async注解時要配置線程池。本文就講述下Springboot應(yīng)用下的線程池配置。
背景知識:Springboot中通過使用ThreadPoolTaskExecutor這個JavaBean對象的corePoolSize(核心線程數(shù))、maxPoolSize(最大線程數(shù))、keepAliveSeconds(線程空閑時長)和queueCapacity(任務(wù)隊列容量)屬性來配置ThreadPoolExecutor,以上四個屬性的作用大致如下:
新提交一個任務(wù)時的處理流程很明顯:
- 如果當(dāng)前線程池的線程數(shù)還沒有達到基本大小(poolSize < corePoolSize),無論是否有空閑的線程新增一個線程處理新提交的任務(wù);
- 如果當(dāng)前線程池的線程數(shù)大于或等于基本大小(poolSize >= corePoolSize) 且任務(wù)隊列未滿時,就將新提交的任務(wù)提交到阻塞隊列排隊,等候處理workQueue.offer(command);
- 如果當(dāng)前線程池的線程數(shù)大于或等于基本大小(poolSize >= corePoolSize) 且任務(wù)隊列滿時;
- 當(dāng)前poolSize<maximumPoolSize,那么就新增線程來處理任務(wù);
- 當(dāng)前poolSize=maximumPoolSize,那么意味著線程池的處理能力已經(jīng)達到了極限,此時需要拒絕新增加的任務(wù)。至于如何拒絕處理新增的任務(wù),取決于線程池的飽和策略RejectedExecutionHandler。
好了,回到正文。目前配置Springboot線程池主要有兩種方式:配置默認線程池和提供自定義線程池;毫無疑問,兩種配置方式并無優(yōu)劣。從使用角度來講,由于自定義線程池是自定義即沒有被Springboot默認使用的線程池,那么就需要通過@Async("自定義線程池bean對象名")的方式去使用,其它地方同默認線程池使用方式一致;下面通過一個簡單的Springboot應(yīng)用結(jié)合實際來展示:
1、新建一個Springboot項目,項目結(jié)構(gòu)和pom.xml內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.17.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.hugesoft</groupId> <artifactId>springboot-async</artifactId> <version>0.0.1</version> <name>springboot-async</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
二、application.yml中,自定義了線程池需要配置的四個屬性,內(nèi)容如下:
task: pool: corePoolSize: 10 maxPoolSize: 20 keepAliveSeconds: 300 queueCapacity: 50
三、在com.hugesoft.config包中有三個類:TaskThreadPoolConfig類用來簡化封裝application.yml配置的屬性,OverrideDefaultThreadPoolConfig類提供了配置默認線程池的方式,CustomizeThreadPoolConfig類則實現(xiàn)了自定義線程池,具體實現(xiàn)如下:
package com.hugesoft.config.dto; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * 線程池配置屬性類 * @author YuXD */ @Data @Component @ConfigurationProperties(prefix = "task.pool") public class TaskThreadPoolConfig { /** * 核心線程數(shù) */ private int corePoolSize; /** * 最大線程數(shù) */ private int maxPoolSize; /** * 線程空閑時間 */ private int keepAliveSeconds; /** * 任務(wù)隊列容量(阻塞隊列) */ private int queueCapacity; }
package com.hugesoft.config; import com.hugesoft.config.dto.TaskThreadPoolConfig; import lombok.extern.slf4j.Slf4j; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; /** * 重寫默認線程池配置 * @author YuXD */ @Slf4j @Configuration @EnableAsync public class OverrideDefaultThreadPoolConfig implements AsyncConfigurer { @Autowired private TaskThreadPoolConfig config; @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //核心線程池大小 executor.setCorePoolSize(config.getCorePoolSize()); //最大線程數(shù) executor.setMaxPoolSize(config.getMaxPoolSize()); //隊列容量 executor.setQueueCapacity(config.getQueueCapacity()); //活躍時間 executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); //線程名字前綴 executor.setThreadNamePrefix("default-thread-"); /* 當(dāng)poolSize已達到maxPoolSize,如何處理新任務(wù)(是拒絕還是交由其它線程處理) CallerRunsPolicy:不在新線程中執(zhí)行任務(wù),而是由調(diào)用者所在的線程來執(zhí)行 */ executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } /** * 異步任務(wù)中異常處理 * * @return */ @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (ex, method, params) -> { log.error("==========================" + ex.getMessage() + "=======================", ex); log.error("exception method:" + method.getName()); }; } }
package com.hugesoft.config; import com.hugesoft.config.dto.TaskThreadPoolConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; /** * * 自定義下城 * @author : YuXD */ @Configuration @EnableAsync public class CustomizeThreadPoolConfig { @Autowired private TaskThreadPoolConfig config; @Bean("customizeThreadPool") public Executor doConfigCustomizeThreadPool() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //核心線程池大小 executor.setCorePoolSize(config.getCorePoolSize()); //最大線程數(shù) executor.setMaxPoolSize(config.getMaxPoolSize()); //隊列容量 executor.setQueueCapacity(config.getQueueCapacity()); //活躍時間 executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); //線程名字前綴 executor.setThreadNamePrefix("customize-thread-"); /* 當(dāng)poolSize已達到maxPoolSize,如何處理新任務(wù)(是拒絕還是交由其它線程處理) CallerRunsPolicy:不在新線程中執(zhí)行任務(wù),而是由調(diào)用者所在的線程來執(zhí)行 */ executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } }
四、com.hugesoft.service包下的內(nèi)容是從真實項目環(huán)境中提取出來的,IStatusAnalyseService定義了設(shè)備狀態(tài)分析基礎(chǔ)Service,JJDeviceDataAnalyseManager,ZHUDeviceDataAnalyseManager,ZZDeviceDataAnalyseManager三個子類分別提供了默認實現(xiàn),AbstractDeviceDataAnalyseManager提取了三個子類用到的公共方法,代碼沒難度,理解即可;需要尤其注意AbstractDeviceDataAnalyseManager的兩個重載方法,分別采用默認線程池和自定義線程池的方式,注意使用的異同點,這點也就是默認線程池和自定義線程池適用上的唯一不同點。具體試下如下:
package com.hugesoft.service; /** * 參數(shù)分析基礎(chǔ)Service,所有需要進行參數(shù)分析的都需要實現(xiàn)該接口 * * @author YuXD */ public interface IStatusAnalyseService { /** * 設(shè)備狀態(tài)解析處理 * * @param start 開始時間 * @param end 截止時間 */ void doStatusAnalyseHandle(String start, String end); /** * 設(shè)備狀態(tài)解析處理 * * @param end 截止時間 */ void doStatusAnalyseHandle(String end); /** * 獲取數(shù)據(jù)類別 * * @return */ String getDataType(); }
package com.hugesoft.service.impl; import com.hugesoft.service.IStatusAnalyseService; import org.springframework.scheduling.annotation.Async; import java.util.Random; /** * 抽象的設(shè)備數(shù)據(jù)分析Manager * * @author YuXD * @since 2020-06-18 22:47 */ public abstract class AbstractDeviceDataAnalyseManager implements IStatusAnalyseService { @Async("customizeThreadPool") @Override public void doStatusAnalyseHandle(String start, String end) { int sleepSeconds = new Random().nextInt(3) + 1; try { Thread.sleep(sleepSeconds * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getDataType() + "在自定義線程" + Thread.currentThread().getName() + "執(zhí)行了" + sleepSeconds + "秒"); } @Async @Override public void doStatusAnalyseHandle(String end) { int sleepSeconds = new Random().nextInt(3) + 1; try { Thread.sleep(sleepSeconds * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getDataType() + "在默認線程" + Thread.currentThread().getName() + "執(zhí)行了" + sleepSeconds + "秒"); } }
package com.hugesoft.service.impl; import org.springframework.stereotype.Service; /** * @description: 機加設(shè)備數(shù)據(jù)分析Service實現(xiàn)類 * @author: YuXD * @create: 2021-03-15 20:17 **/ @Service("JJ") public class JJDeviceDataAnalyseManager extends AbstractDeviceDataAnalyseManager { @Override public String getDataType() { return "機加"; } }
package com.hugesoft.service.impl; import org.springframework.stereotype.Service; /** * @description: 鑄造設(shè)備數(shù)據(jù)分析Service實現(xiàn)類 * @author: YuXD * @create: 2020-06-18 22:56 **/ @Service("ZHU") public class ZHUDeviceDataAnalyseManager extends AbstractDeviceDataAnalyseManager { @Override public String getDataType() { return "鑄造"; } }
package com.hugesoft.service.impl; import org.springframework.stereotype.Service; /** * @description: 總裝設(shè)備數(shù)據(jù)分析Service實現(xiàn)類 * @author: YuXD * @create: 2020-06-18 22:56 **/ @Service("ZZ") public class ZZDeviceDataAnalyseManager extends AbstractDeviceDataAnalyseManager { @Override public String getDataType() { return "總裝"; } }
五、最后看一下Springboot啟動類實現(xiàn);該類既是啟動類也是Controller類,沒什么特別要說明的。
package com.hugesoft; import com.hugesoft.service.IStatusAnalyseService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @EnableAsync @SpringBootApplication public class SpringbootAsyncApplication { @Autowired private List<IStatusAnalyseService> statusAnalyseServiceList; public static void main(String[] args) { SpringApplication.run(SpringbootAsyncApplication.class, args); } @GetMapping("/sayHelloAsync") public String sayHelloAsync() { for (IStatusAnalyseService statusAnalyseService : statusAnalyseServiceList) { // 采用自定義線程池 statusAnalyseService.doStatusAnalyseHandle(null, null); // 采用默認線程池 statusAnalyseService.doStatusAnalyseHandle(null); } return "Hello, Async!"; } }
六、最后啟動main方法,通過瀏覽器地址欄訪問 http://localhost:8080/sayHelloAsync,發(fā)現(xiàn)秒出現(xiàn)如下頁面,且控制臺會出現(xiàn)如下內(nèi)容,說明我們配置的默認線程池和自定義線程池都起作用了,到此,配置成功
到此這篇關(guān)于Springboot應(yīng)用中線程池配置教程(2021版)的文章就介紹到這了,更多相關(guān)Springboot線程池配置內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringMVC Json自定義序列化和反序列化的操作方法
這篇文章主要介紹了SpringMVC Json自定義序列化和反序列化的操作方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01Jackson反序列化@JsonFormat 不生效的解決方案
這篇文章主要介紹了Jackson反序列化@JsonFormat 不生效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08Spring @async方法如何添加注解實現(xiàn)異步調(diào)用
這篇文章主要介紹了Spring @async方法如何添加注解實現(xiàn)異步調(diào)用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-01-01解決org.apache.ibatis.binding.BindingException:?Invalid?boun
這篇文章主要介紹了解決org.apache.ibatis.binding.BindingException:?Invalid?bound?statement?(not?found)問題,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05