Spring使用ThreadPoolTaskExecutor自定義線程池及異步調(diào)用方式
多線程一直是工作或面試過程中的高頻知識(shí)點(diǎn),今天給大家分享一下使用 ThreadPoolTaskExecutor 來自定義線程池和實(shí)現(xiàn)異步調(diào)用多線程。
一、ThreadPoolTaskExecutor
本文采用 Executors 的工廠方法進(jìn)行配置。
1、將線程池用到的參數(shù)定義到配置文件中
在項(xiàng)目的 resources 目錄下創(chuàng)建 executor.properties 文件,并添加如下配置:
# 異步線程配置 # 核心線程數(shù) async.executor.thread.core_pool_size=5 # 最大線程數(shù) async.executor.thread.max_pool_size=8 # 任務(wù)隊(duì)列大小 async.executor.thread.queue_capacity=2 # 線程池中線程的名稱前綴 async.executor.thread.name.prefix=async-service- # 緩沖隊(duì)列中線程的空閑時(shí)間 async.executor.thread.keep_alive_seconds=100
2、Executors 的工廠配置
2.1、配置詳情
@Configuration
// @PropertySource是找的target目錄下classes目錄下的文件,resources目錄下的文件編譯后會(huì)生成在classes目錄
@PropertySource(value = {"classpath:executor.properties"}, ignoreResourceNotFound=false, encoding="UTF-8")
@Slf4j
public class ExecutorConfig {
@Value("${async.executor.thread.core_pool_size}")
private int corePoolSize;
@Value("${async.executor.thread.max_pool_size}")
private int maxPoolSize;
@Value("${async.executor.thread.queue_capacity}")
private int queueCapacity;
@Value("${async.executor.thread.name.prefix}")
private String namePrefix;
@Value("${async.executor.thread.keep_alive_seconds}")
private int keepAliveSeconds;
@Bean(name = "asyncTaskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
log.info("啟動(dòng)");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心線程數(shù)
executor.setCorePoolSize(corePoolSize);
// 最大線程數(shù)
executor.setMaxPoolSize(maxPoolSize);
// 任務(wù)隊(duì)列大小
executor.setQueueCapacity(queueCapacity);
// 線程前綴名
executor.setThreadNamePrefix(namePrefix);
// 線程的空閑時(shí)間
executor.setKeepAliveSeconds(keepAliveSeconds);
// 拒絕策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 線程初始化
executor.initialize();
return executor;
}
}2.2、注解說明
@Configuration:Spring 容器在啟動(dòng)時(shí),會(huì)加載帶有 @Configuration 注解的類,對(duì)其中帶有 @Bean 注解的方法進(jìn)行處理。@Bean:是一個(gè)方法級(jí)別上的注解,主要用在 @Configuration 注解的類里,也可以用在 @Component 注解的類里。添加的 bean 的 id 為方法名。@PropertySource:加載指定的配置文件。value 值為要加載的配置文件,ignoreResourceNotFound 意思是如果加載的文件找不到,程序是否忽略它。默認(rèn)為 false 。如果為 true ,則代表加載的配置文件不存在,程序不報(bào)錯(cuò)。在實(shí)際項(xiàng)目開發(fā)中,最好設(shè)置為 false 。如果 application.properties 文件中的屬性與自定義配置文件中的屬性重復(fù),則自定義配置文件中的屬性值被覆蓋,加載的是 application.properties 文件中的配置屬性。@Slf4j:lombok 的日志輸出工具,加上此注解后,可直接調(diào)用 log 輸出各個(gè)級(jí)別的日志。@Value:調(diào)用配置文件中的屬性并給屬性賦予值。
2.3、線程池配置說明
- 核心線程數(shù):線程池創(chuàng)建時(shí)候初始化的線程數(shù)。當(dāng)線程數(shù)超過核心線程數(shù),則超過的線程則進(jìn)入任務(wù)隊(duì)列。
- 最大線程數(shù):只有在任務(wù)隊(duì)列滿了之后才會(huì)申請(qǐng)超過核心線程數(shù)的線程。不能小于核心線程數(shù)。
- 任務(wù)隊(duì)列:線程數(shù)大于核心線程數(shù)的部分進(jìn)入任務(wù)隊(duì)列。如果任務(wù)隊(duì)列足夠大,超出核心線程數(shù)的線程不會(huì)被創(chuàng)建,它會(huì)等待核心線程執(zhí)行完它們自己的任務(wù)后再執(zhí)行任務(wù)隊(duì)列的任務(wù),而不會(huì)再額外地創(chuàng)建線程。舉例:如果有20個(gè)任務(wù)要執(zhí)行,核心線程數(shù):10,最大線程數(shù):20,任務(wù)隊(duì)列大?。?。則系統(tǒng)會(huì)創(chuàng)建18個(gè)線程。這18個(gè)線程有執(zhí)行完任務(wù)的,再執(zhí)行任務(wù)隊(duì)列中的任務(wù)。
- 線程的空閑時(shí)間:當(dāng) 線程池中的線程數(shù)量 大于 核心線程數(shù) 時(shí),如果某線程空閑時(shí)間超過 keepAliveTime ,線程將被終止。這樣,線程池可以動(dòng)態(tài)的調(diào)整池中的線程數(shù)。
- 拒絕策略:如果(總?cè)蝿?wù)數(shù) - 核心線程數(shù) - 任務(wù)隊(duì)列數(shù))-(最大線程數(shù) - 核心線程數(shù))> 0 的話,則會(huì)出現(xiàn)線程拒絕。舉例:( 12 - 5 - 2 ) - ( 8 - 5 ) > 0,會(huì)出現(xiàn)線程拒絕。線程拒絕又分為 4 種策略,分別為:
CallerRunsPolicy():交由調(diào)用方線程運(yùn)行,比如 main 線程。AbortPolicy():直接拋出異常。DiscardPolicy():直接丟棄。DiscardOldestPolicy():丟棄隊(duì)列中最老的任務(wù)。
2.4、線程池配置個(gè)人理解
- 當(dāng)一個(gè)任務(wù)被提交到線程池時(shí),首先查看線程池的核心線程是否都在執(zhí)行任務(wù)。如果沒有,則選擇一條線程執(zhí)行任務(wù)。
- 如果都在執(zhí)行任務(wù),查看任務(wù)隊(duì)列是否已滿。如果不滿,則將任務(wù)存儲(chǔ)在任務(wù)隊(duì)列中。核心線程執(zhí)行完自己的任務(wù)后,會(huì)再處理任務(wù)隊(duì)列中的任務(wù)。
- 如果任務(wù)隊(duì)列已滿,查看線程池(最大線程數(shù)控制)是否已滿。如果不滿,則創(chuàng)建一條線程去執(zhí)行任務(wù)。如果滿了,就按照策略處理無法執(zhí)行的任務(wù)。
二、異步調(diào)用線程
通常 ThreadPoolTaskExecutor 是和 @Async 一起使用。在一個(gè)方法上添加 @Async 注解,表明是異步調(diào)用方法函數(shù)。
@Async 后面加上線程池的方法名或 bean 名稱,表明異步線程會(huì)加載線程池的配置。
@Component
@Slf4j
public class ThreadTest {
/**
* 每10秒循環(huán)一次,一個(gè)線程共循環(huán)10次。
*/
@Async("asyncTaskExecutor")
public void ceshi3() {
for (int i = 0; i <= 10; i ) {
log.info("ceshi3: " i);
try {
Thread.sleep(2000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}備注:一定要在啟動(dòng)類上添加 @EnableAsync 注解,這樣 @Async 注解才會(huì)生效。
三、多線程使用場(chǎng)景
1、定時(shí)任務(wù) @Scheduled
// 在啟動(dòng)類上添加 @EnableScheduling 注解
@SpringBootApplication
@EnableScheduling
public class SpringBootStudyApplication {
? ?public static void main(String[] args) {
? ? ? SpringApplication.run(SpringBootStudyApplication.class, args);
? ?}
}// @Component 注解將定時(shí)任務(wù)類納入 spring bean 管理。
@Component
public class listennerTest3 {
? ? @Autowired
? ? private ThreadTest t;
? ??
? ? // 每1分鐘執(zhí)行一次ceshi3()方法
? ? @Scheduled(cron = "0 0/1 * * * ?")
? ? public void run() {
? ? ? ? t.ceshi3();
? ? }
}ceshi3() 方法調(diào)用線程池配置,且異步執(zhí)行。
@Component
@Slf4j
public class ThreadTest {
? ? /**
? ? ?* 每10秒循環(huán)一次,一個(gè)線程共循環(huán)10次。
? ? ?*/
? ? @Async("asyncTaskExecutor")
? ? public void ceshi3() {
? ? ? ? for (int i = 0; i <= 10; i ?) {
? ? ? ? ? ? log.info("ceshi3: " ? i);
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? Thread.sleep(2000 * 5);
? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? }
}2、程序一啟動(dòng)就異步執(zhí)行多線程
通過繼承 CommandLineRunner 類實(shí)現(xiàn)。
@Component
public class ListennerTest implements CommandLineRunner {
? ? @Autowired
? ? private ThreadTest t;
? ? @Override
? ? public void run(String... args) {
? ? ? ? for (int i = 1; i <= 10; i ?) {
? ? ? ? ? ? t.ceshi();
? ? ? ? }
? ? }
}@Component
@Slf4j
public class ThreadTest {
? ? @Async("asyncTaskExecutor")
? ? public void ceshi() {
? ? ? ? log.info("ceshi");
? ? }
} ? ?3、定義一個(gè) http 接口
還可以通過接口的形式來異步調(diào)用多線程:
@RestController
@RequestMapping("thread")
public class ListennerTest2 {
? ? @Autowired
? ? private ThreadTest t;
? ? @GetMapping("ceshi2")
? ? public void run() {
? ? ? ? for (int i = 1; i < 10; i ?) {
? ? ? ? ? ? t.ceshi2();
? ? ? ? }
? ? }
}@Component
@Slf4j
public class ThreadTest {
? ? @Async("asyncTaskExecutor")
? ? public void ceshi2() {
? ? ? ? for (int i = 0; i <= 3; i ?) {
? ? ? ? ? ? log.info("ceshi2");
? ? ? ? }
? ? }
} ? ?4、測(cè)試類
@RunWith(SpringRunner.class)
@SpringBootTest
public class ThreadRunTest {
? ? @Autowired
? ? private ThreadTest t;
? ? @Test
? ? public void thread1() {
? ? ? ? for (int i = 1; i <= 10; i ?) {
? ? ? ? ? ? t.ceshi4();
? ? ? ? }
? ? }
}@Component
@Slf4j
public class ThreadTest {
? ? @Async("asyncTaskExecutor")
? ? public void ceshi4() {
? ? ? ? log.info("ceshi4");
? ? }
}四、總結(jié)
以上主要介紹了 ThreadPoolTaskExecutor 線程池的配置、使用、相關(guān)注解的意義及作用,也簡(jiǎn)單介紹了使用 @Async 來異步調(diào)用線程,最后又列舉了多線程的使用場(chǎng)景,并配上了代碼示例。這些僅為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java中實(shí)現(xiàn)Comparable接口實(shí)現(xiàn)自定義排序的示例
下面小編就為大家?guī)硪黄猨ava中實(shí)現(xiàn)Comparable接口實(shí)現(xiàn)自定義排序的示例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09
Mybatis之Mapper動(dòng)態(tài)代理實(shí)例解析
這篇文章主要介紹了Mybatis之Mapper動(dòng)態(tài)代理實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08
java8中的lambda表達(dá)式,看這篇絕對(duì)夠
這篇文章主要介紹了java8中的lambda表達(dá)式,看這篇絕對(duì)夠!具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
深入了解Java中循環(huán)結(jié)構(gòu)的使用
Java中有三種主要的循環(huán)結(jié)構(gòu):while 循環(huán)、do…while 循環(huán)和for 循環(huán)。本文將來和大家一起講講Java中這三個(gè)循環(huán)的使用,需要的可以參考一下2022-08-08
SpringBoot Security實(shí)現(xiàn)單點(diǎn)登出并清除所有token
Spring Security是一個(gè)功能強(qiáng)大且高度可定制的身份驗(yàn)證和訪問控制框架。提供了完善的認(rèn)證機(jī)制和方法級(jí)的授權(quán)功能。是一款非常優(yōu)秀的權(quán)限管理框架。它的核心是一組過濾器鏈,不同的功能經(jīng)由不同的過濾器2023-01-01
SpringBoot+slf4j實(shí)現(xiàn)全鏈路調(diào)用日志跟蹤的方法(一)
本文重點(diǎn)給大家介紹Tracer集成的slf4j MDC功能,方便用戶在只簡(jiǎn)單修改日志配置文件的前提下輸出當(dāng)前 Tracer 上下文 TraceId,文章通過代碼給大家講解了在springboot中使用的技巧,感興趣的朋友跟隨小編一起看看吧2021-05-05
Java語言實(shí)現(xiàn)簡(jiǎn)單FTP軟件 FTP軟件效果圖預(yù)覽之下載功能(2)
這篇文章主要為大家詳細(xì)介紹了Java語言實(shí)現(xiàn)簡(jiǎn)單FTP軟件,F(xiàn)TP軟件效果圖預(yù)覽之下載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
javamail實(shí)現(xiàn)注冊(cè)激活郵件
這篇文章主要為大家詳細(xì)介紹了javamail實(shí)現(xiàn)注冊(cè)激活郵件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04

