您好,登錄后才能下訂單哦!
這篇文章主要介紹了Spring中怎么使用ThreadPoolTaskExecutor自定義線程池及異步調用的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Spring中怎么使用ThreadPoolTaskExecutor自定義線程池及異步調用文章都會有所收獲,下面我們一起來看看吧。
多線程一直是工作或面試過程中的高頻知識點,今天給大家分享一下使用 ThreadPoolTaskExecutor 來自定義線程池和實現異步調用多線程。
本文采用 Executors 的工廠方法進行配置。
在項目的 resources 目錄下創建 executor.properties 文件,并添加如下配置:
# 異步線程配置 # 核心線程數 async.executor.thread.core_pool_size=5 # 最大線程數 async.executor.thread.max_pool_size=8 # 任務隊列大小 async.executor.thread.queue_capacity=2 # 線程池中線程的名稱前綴 async.executor.thread.name.prefix=async-service- # 緩沖隊列中線程的空閑時間 async.executor.thread.keep_alive_seconds=100
@Configuration // @PropertySource是找的target目錄下classes目錄下的文件,resources目錄下的文件編譯后會生成在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("啟動"); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 核心線程數 executor.setCorePoolSize(corePoolSize); // 最大線程數 executor.setMaxPoolSize(maxPoolSize); // 任務隊列大小 executor.setQueueCapacity(queueCapacity); // 線程前綴名 executor.setThreadNamePrefix(namePrefix); // 線程的空閑時間 executor.setKeepAliveSeconds(keepAliveSeconds); // 拒絕策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 線程初始化 executor.initialize(); return executor; } }
@Configuration
:Spring 容器在啟動時,會加載帶有 @Configuration 注解的類,對其中帶有 @Bean 注解的方法進行處理。
@Bean
:是一個方法級別上的注解,主要用在 @Configuration 注解的類里,也可以用在 @Component 注解的類里。添加的 bean 的 id 為方法名。
@PropertySource
:加載指定的配置文件。value 值為要加載的配置文件,ignoreResourceNotFound 意思是如果加載的文件找不到,程序是否忽略它。默認為 false 。如果為 true ,則代表加載的配置文件不存在,程序不報錯。在實際項目開發中,最好設置為 false 。如果 application.properties 文件中的屬性與自定義配置文件中的屬性重復,則自定義配置文件中的屬性值被覆蓋,加載的是 application.properties 文件中的配置屬性。
@Slf4j
:lombok 的日志輸出工具,加上此注解后,可直接調用 log 輸出各個級別的日志。
@Value
:調用配置文件中的屬性并給屬性賦予值。
核心線程數:線程池創建時候初始化的線程數。當線程數超過核心線程數,則超過的線程則進入任務隊列。
最大線程數:只有在任務隊列滿了之后才會申請超過核心線程數的線程。不能小于核心線程數。
任務隊列:線程數大于核心線程數的部分進入任務隊列。如果任務隊列足夠大,超出核心線程數的線程不會被創建,它會等待核心線程執行完它們自己的任務后再執行任務隊列的任務,而不會再額外地創建線程。舉例:如果有20個任務要執行,核心線程數:10,最大線程數:20,任務隊列大小:2。則系統會創建18個線程。這18個線程有執行完任務的,再執行任務隊列中的任務。
線程的空閑時間:當 線程池中的線程數量 大于 核心線程數 時,如果某線程空閑時間超過 keepAliveTime ,線程將被終止。這樣,線程池可以動態的調整池中的線程數。
拒絕策略:如果(總任務數 - 核心線程數 - 任務隊列數)-(最大線程數 - 核心線程數)> 0 的話,則會出現線程拒絕。舉例:( 12 - 5 - 2 ) - ( 8 - 5 ) > 0,會出現線程拒絕。線程拒絕又分為 4 種策略,分別為:
CallerRunsPolicy()
:交由調用方線程運行,比如 main 線程。
AbortPolicy()
:直接拋出異常。
DiscardPolicy()
:直接丟棄。
DiscardOldestPolicy()
:丟棄隊列中最老的任務。
當一個任務被提交到線程池時,首先查看線程池的核心線程是否都在執行任務。如果沒有,則選擇一條線程執行任務。
如果都在執行任務,查看任務隊列是否已滿。如果不滿,則將任務存儲在任務隊列中。核心線程執行完自己的任務后,會再處理任務隊列中的任務。
如果任務隊列已滿,查看線程池(最大線程數控制)是否已滿。如果不滿,則創建一條線程去執行任務。如果滿了,就按照策略處理無法執行的任務。
通常 ThreadPoolTaskExecutor 是和 @Async 一起使用。在一個方法上添加 @Async 注解,表明是異步調用方法函數。
@Async 后面加上線程池的方法名或 bean 名稱,表明異步線程會加載線程池的配置。
@Component @Slf4j public class ThreadTest { /** * 每10秒循環一次,一個線程共循環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(); } } } }
備注:一定要在啟動類上添加 @EnableAsync 注解,這樣 @Async 注解才會生效。
// 在啟動類上添加 @EnableScheduling 注解 @SpringBootApplication @EnableScheduling public class SpringBootStudyApplication { public static void main(String[] args) { SpringApplication.run(SpringBootStudyApplication.class, args); } }
// @Component 注解將定時任務類納入 spring bean 管理。 @Component public class listennerTest3 { @Autowired private ThreadTest t; // 每1分鐘執行一次ceshi3()方法 @Scheduled(cron = "0 0/1 * * * ?") public void run() { t.ceshi3(); } }
ceshi3() 方法調用線程池配置,且異步執行。
@Component @Slf4j public class ThreadTest { /** * 每10秒循環一次,一個線程共循環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(); } } } }
通過繼承 CommandLineRunner 類實現。
@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"); } }
還可以通過接口的形式來異步調用多線程:
@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"); } } }
@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"); } }
關于“Spring中怎么使用ThreadPoolTaskExecutor自定義線程池及異步調用”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Spring中怎么使用ThreadPoolTaskExecutor自定義線程池及異步調用”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。