多線程一直是工做或面試過程當中的高頻知識點,今天給你們分享一下使用 ThreadPoolTaskExecutor 來自定義線程池和實現異步調用多線程。java
本文采用 Executors 的工廠方法進行配置。面試
在項目的 resources 目錄下建立 executor.properties 文件,並添加以下配置:spring
# 異步線程配置 # 核心線程數 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; } }
**核心線程數:**線程池建立時候初始化的線程數。當線程數超過核心線程數,則超過的線程則進入任務隊列。微信
**最大線程數:**只有在任務隊列滿了以後纔會申請超過核心線程數的線程。不能小於核心線程數。多線程
**任務隊列:**線程數大於核心線程數的部分進入任務隊列。若是任務隊列足夠大,超出核心線程數的線程不會被建立,它會等待覈心線程執行完它們本身的任務後再執行任務隊列的任務,而不會再額外地建立線程。**舉例:**若是有20個任務要執行,核心線程數:10,最大線程數:20,任務隊列大小:2。則系統會建立18個線程。這18個線程有執行完任務的,再執行任務隊列中的任務。app
**線程的空閒時間:**當 線程池中的線程數量 大於 核心線程數 時,若是某線程空閒時間超過 keepAliveTime ,線程將被終止。這樣,線程池能夠動態的調整池中的線程數。異步
**拒絕策略:**若是(總任務數 - 核心線程數 - 任務隊列數)-(最大線程數 - 核心線程數)> 0 的話,則會出現線程拒絕。舉例:( 12 - 5 - 2 ) - ( 8 - 5 ) > 0,會出現線程拒絕。線程拒絕又分爲 4 種策略,分別爲:async
一般 ThreadPoolTaskExecutor 是和 @Async 一塊兒使用。在一個方法上添加 @Async 註解,代表是異步調用方法函數。@Async 後面加上線程池的方法名或 bean 名稱,代表異步線程會加載線程池的配置。ide
@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"); } }
以上主要介紹了 ThreadPoolTaskExecutor 線程池的配置、使用、相關注解的意義及做用,也簡單介紹了使用 @Async 來異步調用線程,最後又列舉了多線程的使用場景,並配上了代碼示例。但願你們喜歡。
本文來自: 微信公衆號【大數據實戰演練】。閱讀更多精彩好文,歡迎關注微信公衆號【大數據實戰演練】。