溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring中怎么使用ThreadPoolTaskExecutor自定義線程池及異步調(diào)用

發(fā)布時間:2022-02-25 15:45:53 來源:億速云 閱讀:413 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹了Spring中怎么使用ThreadPoolTaskExecutor自定義線程池及異步調(diào)用的相關(guān)知識,內(nèi)容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Spring中怎么使用ThreadPoolTaskExecutor自定義線程池及異步調(diào)用文章都會有所收獲,下面我們一起來看看吧。

    多線程一直是工作或面試過程中的高頻知識點,今天給大家分享一下使用 ThreadPoolTaskExecutor 來自定義線程池和實現(xiàn)異步調(diào)用多線程。

    一、ThreadPoolTaskExecutor

    本文采用 Executors 的工廠方法進行配置。

    1、將線程池用到的參數(shù)定義到配置文件中

    在項目的 resources 目錄下創(chuàng)建 executor.properties 文件,并添加如下配置:

    # 異步線程配置
    # 核心線程數(shù)
    async.executor.thread.core_pool_size=5
    # 最大線程數(shù)
    async.executor.thread.max_pool_size=8
    # 任務(wù)隊列大小
    async.executor.thread.queue_capacity=2
    # 線程池中線程的名稱前綴
    async.executor.thread.name.prefix=async-service-
    # 緩沖隊列中線程的空閑時間
    async.executor.thread.keep_alive_seconds=100

    2、Executors 的工廠配置

    2.1、配置詳情
    @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();
            // 核心線程數(shù)
            executor.setCorePoolSize(corePoolSize);
            // 最大線程數(shù)
            executor.setMaxPoolSize(maxPoolSize);
            // 任務(wù)隊列大小
            executor.setQueueCapacity(queueCapacity);
            // 線程前綴名
            executor.setThreadNamePrefix(namePrefix);
            // 線程的空閑時間
            executor.setKeepAliveSeconds(keepAliveSeconds);
            // 拒絕策略
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            // 線程初始化
            executor.initialize();
            return executor;
        }
    }
    2.2、注解說明
    • @Configuration:Spring 容器在啟動時,會加載帶有 @Configuration 注解的類,對其中帶有 @Bean 注解的方法進行處理。

    • @Bean:是一個方法級別上的注解,主要用在 @Configuration 注解的類里,也可以用在 @Component 注解的類里。添加的 bean 的 id 為方法名。

    • @PropertySource:加載指定的配置文件。value 值為要加載的配置文件,ignoreResourceNotFound 意思是如果加載的文件找不到,程序是否忽略它。默認為 false 。如果為 true ,則代表加載的配置文件不存在,程序不報錯。在實際項目開發(fā)中,最好設(shè)置為 false 。如果 application.properties 文件中的屬性與自定義配置文件中的屬性重復(fù),則自定義配置文件中的屬性值被覆蓋,加載的是 application.properties 文件中的配置屬性。

    • @Slf4j:lombok 的日志輸出工具,加上此注解后,可直接調(diào)用 log 輸出各個級別的日志。

    • @Value:調(diào)用配置文件中的屬性并給屬性賦予值。

    2.3、線程池配置說明
    • 核心線程數(shù):線程池創(chuàng)建時候初始化的線程數(shù)。當(dāng)線程數(shù)超過核心線程數(shù),則超過的線程則進入任務(wù)隊列。

    • 最大線程數(shù):只有在任務(wù)隊列滿了之后才會申請超過核心線程數(shù)的線程。不能小于核心線程數(shù)。

    • 任務(wù)隊列:線程數(shù)大于核心線程數(shù)的部分進入任務(wù)隊列。如果任務(wù)隊列足夠大,超出核心線程數(shù)的線程不會被創(chuàng)建,它會等待核心線程執(zhí)行完它們自己的任務(wù)后再執(zhí)行任務(wù)隊列的任務(wù),而不會再額外地創(chuàng)建線程。舉例:如果有20個任務(wù)要執(zhí)行,核心線程數(shù):10,最大線程數(shù):20,任務(wù)隊列大?。?。則系統(tǒng)會創(chuàng)建18個線程。這18個線程有執(zhí)行完任務(wù)的,再執(zhí)行任務(wù)隊列中的任務(wù)。

    • 線程的空閑時間:當(dāng) 線程池中的線程數(shù)量 大于 核心線程數(shù) 時,如果某線程空閑時間超過 keepAliveTime ,線程將被終止。這樣,線程池可以動態(tài)的調(diào)整池中的線程數(shù)。

    • 拒絕策略:如果(總?cè)蝿?wù)數(shù) - 核心線程數(shù) - 任務(wù)隊列數(shù))-(最大線程數(shù) - 核心線程數(shù))> 0 的話,則會出現(xiàn)線程拒絕。舉例:( 12 - 5 - 2 ) - ( 8 - 5 ) > 0,會出現(xiàn)線程拒絕。線程拒絕又分為 4 種策略,分別為:

    • CallerRunsPolicy():交由調(diào)用方線程運行,比如 main 線程。

    • AbortPolicy():直接拋出異常。

    • DiscardPolicy():直接丟棄。

    • DiscardOldestPolicy():丟棄隊列中最老的任務(wù)。

    2.4、線程池配置個人理解
    • 當(dāng)一個任務(wù)被提交到線程池時,首先查看線程池的核心線程是否都在執(zhí)行任務(wù)。如果沒有,則選擇一條線程執(zhí)行任務(wù)。

    • 如果都在執(zhí)行任務(wù),查看任務(wù)隊列是否已滿。如果不滿,則將任務(wù)存儲在任務(wù)隊列中。核心線程執(zhí)行完自己的任務(wù)后,會再處理任務(wù)隊列中的任務(wù)。

    • 如果任務(wù)隊列已滿,查看線程池(最大線程數(shù)控制)是否已滿。如果不滿,則創(chuàng)建一條線程去執(zhí)行任務(wù)。如果滿了,就按照策略處理無法執(zhí)行的任務(wù)。

    二、異步調(diào)用線程

    通常 ThreadPoolTaskExecutor 是和 @Async 一起使用。在一個方法上添加 @Async 注解,表明是異步調(diào)用方法函數(shù)。

    @Async 后面加上線程池的方法名或 bean 名稱,表明異步線程會加載線程池的配置。

    @Component
    @Slf4j
    public class ThreadTest {
        /**
         * 每10秒循環(huán)一次,一個線程共循環(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();
                }
            }
        }
    }

    備注:一定要在啟動類上添加 @EnableAsync 注解,這樣 @Async 注解才會生效。

    三、多線程使用場景

    1、定時任務(wù) @Scheduled

    // 在啟動類上添加 @EnableScheduling 注解
    @SpringBootApplication
    @EnableScheduling
    public class SpringBootStudyApplication {
       public static void main(String[] args) {
          SpringApplication.run(SpringBootStudyApplication.class, args);
       }
    }
    // @Component 注解將定時任務(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)一次,一個線程共循環(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、程序一啟動就異步執(zhí)行多線程

    通過繼承 CommandLineRunner 類實現(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、定義一個 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、測試類

    @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");
        }
    }

    關(guān)于“Spring中怎么使用ThreadPoolTaskExecutor自定義線程池及異步調(diào)用”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“Spring中怎么使用ThreadPoolTaskExecutor自定義線程池及異步調(diào)用”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI