Springboot?配置線(xiàn)程池創(chuàng)建線(xiàn)程及配置?@Async?異步操作線(xiàn)程池詳解
前言
眾所周知,創(chuàng)建顯示線(xiàn)程和直接使用未配置的線(xiàn)程池創(chuàng)建線(xiàn)程,都會(huì)被阿里的大佬給diss,所以我們要規(guī)范的創(chuàng)建線(xiàn)程。
至于 @Async 異步任務(wù)的用處是不想等待方法執(zhí)行完就返回結(jié)果,提高軟件前臺(tái)響應(yīng)速度,一個(gè)程序中會(huì)用到很多異步方法,所以需要使用線(xiàn)程池管理,防止影響性能。
一、創(chuàng)建一個(gè)Springboot Web項(xiàng)目
需要一個(gè)Springboot項(xiàng)目
二、新建ThreadPoolConfig
- 可以直接return一個(gè)內(nèi)置線(xiàn)程池
- Executors類(lèi)創(chuàng)建線(xiàn)程池的方法歸根結(jié)底都是調(diào)用ThreadPoolExecutor類(lèi),只不過(guò)對(duì)每個(gè)方法賦值不同的參數(shù)去構(gòu)造ThreadPoolExecutor對(duì)象。
- newCachedThreadPool:創(chuàng)建一個(gè)可緩存的線(xiàn)程池,如果線(xiàn)程池長(zhǎng)度超過(guò)處理需要,可靈活回收空閑線(xiàn)程,若無(wú)可回收,則新建線(xiàn)程。
- newFixedThreadPool: 創(chuàng)建一個(gè)定長(zhǎng)線(xiàn)程池,可控制線(xiàn)程最大并發(fā)數(shù),超出的線(xiàn)程會(huì)在隊(duì)列中等待
- newScheduledThreadPool: 創(chuàng)建一個(gè)定長(zhǎng)線(xiàn)程池,支持定時(shí)及周期性任務(wù)執(zhí)行。
- newSingleThreadExecutor: 創(chuàng)建一個(gè)單線(xiàn)程化的線(xiàn)程池,它只會(huì)用唯一的工作線(xiàn)程來(lái)執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行。
也可以自己new一個(gè)ThreadPoolExecutor自定義參數(shù)
參數(shù)說(shuō)明:
- corePoolSize: 常駐核心線(xiàn)程數(shù),如果大于0,即使本地任務(wù)執(zhí)行完也不會(huì)被銷(xiāo)毀
- maximumPoolSize: 線(xiàn)程池能夠容納可同時(shí)執(zhí)行的最大線(xiàn)程數(shù)
- keepAliveTime: 線(xiàn)程池中線(xiàn)程空閑的時(shí)間,當(dāng)空閑時(shí)間達(dá)到該值時(shí),線(xiàn)程會(huì)被銷(xiāo)毀, 只剩下 corePoolSize 個(gè)線(xiàn)程數(shù)量。
- unit: 空閑時(shí)間的單位。一般以TimeUnit類(lèi)定義時(shí)分秒。
- workQueue: 當(dāng)請(qǐng)求的線(xiàn)程數(shù)大于 corePoolSize 時(shí),線(xiàn)程進(jìn)入該阻塞隊(duì)列。
- threadFactory: 線(xiàn)程工廠(chǎng),用來(lái)生產(chǎn)一組相同任務(wù)的線(xiàn)程,同時(shí)也可以通過(guò)它增加前綴名,虛擬機(jī)棧分析時(shí)更清晰
- handler: 執(zhí)行拒絕策略,當(dāng) workQueue 已滿(mǎn),且超過(guò)maximumPoolSize 最大值,就要通過(guò)這個(gè)來(lái)處理,比如拒絕,丟棄等,這是一種限流的保護(hù)措施。
阻塞隊(duì)列的實(shí)現(xiàn)類(lèi):
- LinkedBlockingQueue 無(wú)界隊(duì)列,當(dāng)不指定隊(duì)列大小時(shí),將會(huì)默認(rèn)為Integer.MAX_VALUE大小的隊(duì)列,因此大量的任務(wù)將會(huì)堆積在隊(duì)列中,最終可能觸發(fā)OOM。
- ArrayBlockingQueue 有界隊(duì)列,基于數(shù)組的先進(jìn)先出隊(duì)列,此隊(duì)列創(chuàng)建時(shí)必須指定大小。
- PriorityBlockingQueue 有界隊(duì)列,基于優(yōu)先級(jí)任務(wù)的,它是通過(guò)Comparator決定的。
- SynchronousQueue 這個(gè)隊(duì)列比較特殊,它不會(huì)保存提交的任務(wù),而是將直接新建一個(gè)線(xiàn)程來(lái)執(zhí)行新來(lái)的任務(wù)
處理策略Handler:
- AbortPolicy 默認(rèn)的拒絕策略,拋RejectedExecutionException異常
- DiscardPolicy 相當(dāng)大膽的策略,直接丟棄任務(wù),沒(méi)有任何異常拋出
- DiscardOldestPolicy 丟棄最老的任務(wù),其實(shí)就是把最早進(jìn)入工作隊(duì)列的任務(wù)丟棄,然后把新任務(wù)加入到工作隊(duì)列
- CallerRunsPolicy 提交任務(wù)的線(xiàn)程自己去執(zhí)行該任務(wù)
線(xiàn)程池的關(guān)閉:
shutdown() : 不會(huì)立刻終止線(xiàn)程,等所有緩存隊(duì)列中的任務(wù)都執(zhí)行完畢后才會(huì)終止。
shutdownNow() : 立即終止線(xiàn)程池,并嘗試打斷正在執(zhí)行的任務(wù),并且清空任務(wù)緩存隊(duì)列,返回尚未執(zhí)行的任務(wù)
package com.xuyijie.threadpooldemo.config; import org.springframework.boot.SpringBootConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.*; /** * @author 徐一杰 * @date 2022/9/20 13:05 * @description 配置線(xiàn)程池 */ @SpringBootConfiguration @EnableAsync public class ThreadPoolConfig { @Bean public ExecutorService getThreadPool(){ ExecutorService threadPool = new ThreadPoolExecutor(2,5, 1L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); return threadPool; // return Executors.newCachedThreadPool(); } /** * 下面的配置是配置Springboot的@Async注解所用的線(xiàn)程池 */ @Bean public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //獲取到cpu內(nèi)核數(shù) int i = Runtime.getRuntime().availableProcessors(); // 設(shè)置線(xiàn)程池核心容量 executor.setCorePoolSize(i); // 設(shè)置線(xiàn)程池最大容量 executor.setMaxPoolSize(i * 2); // 設(shè)置任務(wù)隊(duì)列長(zhǎng)度 executor.setQueueCapacity(200); // 設(shè)置線(xiàn)程超時(shí)時(shí)間 executor.setKeepAliveSeconds(60); // 設(shè)置線(xiàn)程名稱(chēng)前綴 executor.setThreadNamePrefix("xyjAsyncPool-"); // 設(shè)置任務(wù)丟棄后的處理策略,當(dāng)poolSize已達(dá)到maxPoolSize,如何處理新任務(wù)(是拒絕還是交由其它線(xiàn)程處理),CallerRunsPolicy:不在新線(xiàn)程中執(zhí)行任務(wù),而是由調(diào)用者所在的線(xiàn)程來(lái)執(zhí) executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } }
使用此線(xiàn)程池時(shí)直接注入ExecutorService,然后:
executorService.execute(() -> { ?try { String message = redisUtil.listRightPop(“queue:queueData”, 5, TimeUnit.SECONDS); System.out.println(“接收到了消息message” + message); } catch (Exception ex) { date.set(simpleDateFormat.format(new Date())); System.out.println(“隊(duì)列阻塞超時(shí)-” + date + ex.getMessage()); } finally { ?date.set(simpleDateFormat.format(new Date())); System.out.println(“線(xiàn)程銷(xiāo)毀-” + date); executorService.shutdown(); } ?});
三、新建controller測(cè)試
package com.xuyijie.threadpooldemo.controller; import com.xuyijie.threadpooldemo.async.AsyncMethod; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.ExecutorService; /** * @author 徐一杰 * @date 2022/9/20 10:30 * @description */ @RestController @RequestMapping("/test") public class TestController { @Autowired private ExecutorService executorService; @Autowired private AsyncMethod asyncMethod; @GetMapping("/helloThread") public void helloThread(){ executorService.execute(() -> { for (int i = 0;i < 100;i++){ System.out.println("111"); } }); executorService.execute(() -> { for (int i = 0;i < 100;i++){ System.out.println("222"); } }); } @GetMapping("/helloAsync") public String helloAsync(){ // 這個(gè)方法是異步的 asyncMethod.print(); System.out.println("print方法還在循環(huán),但我已經(jīng)可以執(zhí)行了"); return "print方法還在循環(huán),但我已經(jīng)可以執(zhí)行了"; } }
AsyncMethod.java
package com.xuyijie.threadpooldemo.async; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; /** * @author 徐一杰 * @date 2022/9/20 15:10 * @description 異步方法 */ @Component public class AsyncMethod { /** * 異步方法,如果@Async加在類(lèi)的上面,則整個(gè)類(lèi)中的方法都是異步的 */ @Async public void print(){ for (int i = 0;i < 100;i++){ System.out.println(i); } } }
四、演示結(jié)果
首先演示 helloThread 這個(gè)接口,創(chuàng)建了2個(gè)線(xiàn)程,發(fā)現(xiàn)他們并發(fā)執(zhí)行,成功
再演示 helloAsync 這個(gè)接口,發(fā)現(xiàn) System.out.println("print方法還在循環(huán),但我已經(jīng)可以執(zhí)行了");
這行代碼無(wú)需等待上面AsyncMethod中的 print 方法執(zhí)行完畢,就可以開(kāi)始執(zhí)行,說(shuō)明 print 方法是異步的,而且我輸出的日志注意看,[xyjAsyncPool - ]
,我設(shè)置的線(xiàn)程池前綴,已經(jīng)生效了,成功
到此這篇關(guān)于Springboot 配置線(xiàn)程池創(chuàng)建線(xiàn)程及配置 @Async 異步操作線(xiàn)程池詳解的文章就介紹到這了,更多相關(guān)Springboot 配置線(xiàn)程池 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java連接mongoDB并進(jìn)行增刪改查操作實(shí)例詳解
這篇文章主要介紹了java連接mongoDB并進(jìn)行增刪改查操作,結(jié)合實(shí)例形式詳細(xì)分析了java環(huán)境下MongoDB擴(kuò)展包的下載、安裝及操作MongoDB連接、增刪改查等相關(guān)操作技巧,需要的朋友可以參考下2019-04-04Flutter ListView 上拉加載更多下拉刷新功能實(shí)現(xiàn)方法
這篇文章主要介紹了Flutter ListView 上拉加載更多下拉刷新功能實(shí)現(xiàn)方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07Springboot 如何實(shí)現(xiàn)filter攔截token驗(yàn)證和跨域
這篇文章主要介紹了Springboot 如何實(shí)現(xiàn)filter攔截token驗(yàn)證和跨域操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Vue3源碼解讀effectScope API及實(shí)現(xiàn)原理
這篇文章主要為大家介紹了Vue3源碼解讀effectScope API及實(shí)現(xiàn)原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03在Java開(kāi)發(fā)中無(wú)法繞開(kāi)的SpringBoot框架詳解
SpringBoot是一個(gè)基于Spring框架的快速開(kāi)發(fā)框架,它的出現(xiàn)極大地簡(jiǎn)化了Spring應(yīng)用的開(kāi)發(fā)流程,SpringBoot是一個(gè)快速開(kāi)發(fā)的框架,它提供了一種快速構(gòu)建應(yīng)用程序的方式,本文給大家介紹在Java開(kāi)發(fā)中無(wú)法繞開(kāi)的框架:SpringBoot,感興趣的朋友一起看看吧2023-09-09java實(shí)現(xiàn)超市商品庫(kù)存管理平臺(tái)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)超市商品庫(kù)存管理平臺(tái),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10