Springboot應用中線程池配置詳細教程(最新2021版)
前言:日常開發中我們常用ThreadPoolExecutor提供的線程池服務幫我們管理線程,在Springboot中更是提供瞭@Async註解來簡化業務邏輯提交到線程池中執行的過程。由於Springboot中默認設置的corePoolSize=1和queyeCapacity=Integer.MAX_VALUE,相當於采用單線程處理所有任務,這就與多線程的目的背道而馳,所以這就要求我們在使用@Async註解時要配置線程池。本文就講述下Springboot應用下的線程池配置。
背景知識:Springboot中通過使用ThreadPoolTaskExecutor這個JavaBean對象的corePoolSize(核心線程數)、maxPoolSize(最大線程數)、keepAliveSeconds(線程空閑時長)和queueCapacity(任務隊列容量)屬性來配置ThreadPoolExecutor,以上四個屬性的作用大致如下:
新提交一個任務時的處理流程很明顯:
- 如果當前線程池的線程數還沒有達到基本大小(poolSize < corePoolSize),無論是否有空閑的線程新增一個線程處理新提交的任務;
- 如果當前線程池的線程數大於或等於基本大小(poolSize >= corePoolSize) 且任務隊列未滿時,就將新提交的任務提交到阻塞隊列排隊,等候處理workQueue.offer(command);
- 如果當前線程池的線程數大於或等於基本大小(poolSize >= corePoolSize) 且任務隊列滿時;
- 當前poolSize<maximumPoolSize,那麼就新增線程來處理任務;
- 當前poolSize=maximumPoolSize,那麼意味著線程池的處理能力已經達到瞭極限,此時需要拒絕新增加的任務。至於如何拒絕處理新增的任務,取決於線程池的飽和策略RejectedExecutionHandler。
好瞭,回到正文。目前配置Springboot線程池主要有兩種方式:配置默認線程池和提供自定義線程池;毫無疑問,兩種配置方式並無優劣。從使用角度來講,由於自定義線程池是自定義即沒有被Springboot默認使用的線程池,那麼就需要通過@Async(“自定義線程池bean對象名”)的方式去使用,其它地方同默認線程池使用方式一致;下面通過一個簡單的Springboot應用結合實際來展示:
1、新建一個Springboot項目,項目結構和pom.xml內容如下:
<?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中,自定義瞭線程池需要配置的四個屬性,內容如下:
task: pool: corePoolSize: 10 maxPoolSize: 20 keepAliveSeconds: 300 queueCapacity: 50
三、在com.hugesoft.config包中有三個類:TaskThreadPoolConfig類用來簡化封裝application.yml配置的屬性,OverrideDefaultThreadPoolConfig類提供瞭配置默認線程池的方式,CustomizeThreadPoolConfig類則實現瞭自定義線程池,具體實現如下:
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 { /** * 核心線程數 */ private int corePoolSize; /** * 最大線程數 */ private int maxPoolSize; /** * 線程空閑時間 */ private int keepAliveSeconds; /** * 任務隊列容量(阻塞隊列) */ 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()); //最大線程數 executor.setMaxPoolSize(config.getMaxPoolSize()); //隊列容量 executor.setQueueCapacity(config.getQueueCapacity()); //活躍時間 executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); //線程名字前綴 executor.setThreadNamePrefix("default-thread-"); /* 當poolSize已達到maxPoolSize,如何處理新任務(是拒絕還是交由其它線程處理) CallerRunsPolicy:不在新線程中執行任務,而是由調用者所在的線程來執行 */ executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } /** * 異步任務中異常處理 * * @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()); //最大線程數 executor.setMaxPoolSize(config.getMaxPoolSize()); //隊列容量 executor.setQueueCapacity(config.getQueueCapacity()); //活躍時間 executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); //線程名字前綴 executor.setThreadNamePrefix("customize-thread-"); /* 當poolSize已達到maxPoolSize,如何處理新任務(是拒絕還是交由其它線程處理) CallerRunsPolicy:不在新線程中執行任務,而是由調用者所在的線程來執行 */ executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } }
四、com.hugesoft.service包下的內容是從真實項目環境中提取出來的,IStatusAnalyseService定義瞭設備狀態分析基礎Service,JJDeviceDataAnalyseManager,ZHUDeviceDataAnalyseManager,ZZDeviceDataAnalyseManager三個子類分別提供瞭默認實現,AbstractDeviceDataAnalyseManager提取瞭三個子類用到的公共方法,代碼沒難度,理解即可;需要尤其註意AbstractDeviceDataAnalyseManager的兩個重載方法,分別采用默認線程池和自定義線程池的方式,註意使用的異同點,這點也就是默認線程池和自定義線程池適用上的唯一不同點。具體試下如下:
package com.hugesoft.service; /** * 參數分析基礎Service,所有需要進行參數分析的都需要實現該接口 * * @author YuXD */ public interface IStatusAnalyseService { /** * 設備狀態解析處理 * * @param start 開始時間 * @param end 截止時間 */ void doStatusAnalyseHandle(String start, String end); /** * 設備狀態解析處理 * * @param end 截止時間 */ void doStatusAnalyseHandle(String end); /** * 獲取數據類別 * * @return */ String getDataType(); }
package com.hugesoft.service.impl; import com.hugesoft.service.IStatusAnalyseService; import org.springframework.scheduling.annotation.Async; import java.util.Random; /** * 抽象的設備數據分析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() + "執行瞭" + 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() + "執行瞭" + sleepSeconds + "秒"); } }
package com.hugesoft.service.impl; import org.springframework.stereotype.Service; /** * @description: 機加設備數據分析Service實現類 * @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: 鑄造設備數據分析Service實現類 * @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: 總裝設備數據分析Service實現類 * @author: YuXD * @create: 2020-06-18 22:56 **/ @Service("ZZ") public class ZZDeviceDataAnalyseManager extends AbstractDeviceDataAnalyseManager { @Override public String getDataType() { return "總裝"; } }
五、最後看一下Springboot啟動類實現;該類既是啟動類也是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,發現秒出現如下頁面,且控制臺會出現如下內容,說明我們配置的默認線程池和自定義線程池都起作用瞭,到此,配置成功
到此這篇關於Springboot應用中線程池配置教程(2021版)的文章就介紹到這瞭,更多相關Springboot線程池配置內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 詳解SpringBoot實現ApplicationEvent事件的監聽與發佈
- springboot @Async 註解如何實現方法異步
- 利用5分鐘快速搭建一個springboot項目的全過程
- eclipse創建springboot項目的三種方式總結
- Springboot 如何使用@Async整合線程池