溫馨提示×

溫馨提示×

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

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

springboot為異步任務規(guī)劃自定義線程池如何實現(xiàn)

發(fā)布時間:2022-06-14 11:51:43 來源:億速云 閱讀:209 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“springboot為異步任務規(guī)劃自定義線程池如何實現(xiàn)”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“springboot為異步任務規(guī)劃自定義線程池如何實現(xiàn)”吧!

一、Spring Boot任務線程池

線程池的作用

  • 防止資源占用無限的擴張

  • 調(diào)用過程省去資源的創(chuàng)建和銷毀所占用的時間

在高并發(fā)環(huán)境下,不斷的分配新資源,可能導致系統(tǒng)資源耗盡。所以為了避免這個問題,我們?yōu)楫惒饺蝿找?guī)劃一個線程池。當然,如果沒有配置線程池的話,springboot會自動配置一個ThreadPoolTaskExecutor 線程池到bean當中。

# 核心線程數(shù)
spring.task.execution.pool.core-size=8  
# 最大線程數(shù)
spring.task.execution.pool.max-size=16
# 空閑線程存活時間
spring.task.execution.pool.keep-alive=60s
# 是否允許核心線程超時
spring.task.execution.pool.allow-core-thread-timeout=true
# 線程隊列數(shù)量
spring.task.execution.pool.queue-capacity=100
# 線程關(guān)閉等待
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
# 線程名稱前綴
spring.task.execution.thread-name-prefix=task-

在springboot配置文件中加入上面的配置,即可實現(xiàn)ThreadPoolTaskExecutor 線程池。

二、自定義線程池

有的時候,我們希望將系統(tǒng)內(nèi)的一類任務放到一個線程池,另一類任務放到另外一個線程池,所以使用Spring Boot自帶的任務線程池就捉襟見肘了。下面介紹自定義線程池的方法。

創(chuàng)建一個 線程池配置類 TaskConfiguration ,并配置一個 任務線程池對象 taskExecutor。

@Configuration
public class TaskConfiguration {
    @Bean("taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(200);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("taskExecutor-");
        executor.setRejectedExecutionHandler(new CallerRunsPolicy());
        return executor;
    }
}

springboot為異步任務規(guī)劃自定義線程池如何實現(xiàn)

上面我們通過使用 ThreadPoolTaskExecutor 創(chuàng)建了一個 線程池,同時設(shè)置了以下這些參數(shù):

線程池屬性屬性的作用上文代碼設(shè)置初始值
核心線程數(shù)CorePoolSize線程池創(chuàng)建時候初始化的線程數(shù),最小線程數(shù)10
最大線程數(shù)MaxPoolSize線程池最大的線程數(shù),只有在緩沖隊列滿了之后,才會申請超過核心線程數(shù)的線程20
緩沖任務隊列QueueCapacity用來緩沖執(zhí)行任務的隊列200
允許線程的空閑時間KeepAliveSeconds超過了核心線程之外的線程,在空閑時間到達之后,沒活干的線程會被銷毀60秒
線程池名的前綴 ThreadNamePrefix可以用于定位處理任務所在的線程池taskExecutor-
線程池對任務的Reject策略RejectedExecutionHandler當線程池運行飽和,或者線程池處于shutdown臨界狀態(tài)時,用來拒絕一個任務的執(zhí)行CallerRunsPolicy

Reject策略預定義有四種:

  • AbortPolicy,用于被拒絕任務的處理程序,它將拋出RejectedExecutionException。

  • CallerRunsPolicy,用于被拒絕任務的處理程序,它直接在execute方法的調(diào)用線程中運行被拒絕的任務。

  • DiscardOldestPolicy,用于被拒絕任務的處理程序,它放棄最舊的未處理請求,然后重試execute。

  • DiscardPolicy,用于被拒絕任務的處理程序,默認情況下它將丟棄被拒絕的任務。

創(chuàng)建 AsyncExecutorTask類,三個任務的配置和 AsyncTask 一樣,不同的是 @Async 注解需要指定前面配置的 線程池的名稱 taskExecutor。

@Component
public class AsyncExecutorTask extends AbstractTask {
    @Async("taskExecutor")
    public Future<String> doTaskOneCallback() throws Exception {
        super.doTaskOne();
        System.out.println("任務一,當前線程:" + Thread.currentThread().getName());
        return new AsyncResult<>("任務一完成");
    }

    @Async("taskExecutor")
    public Future<String> doTaskTwoCallback() throws Exception {
        super.doTaskTwo();
        System.out.println("任務二,當前線程:" + Thread.currentThread().getName());
        return new AsyncResult<>("任務二完成");
    }

    @Async("taskExecutor")
    public Future<String> doTaskThreeCallback() throws Exception {
        super.doTaskThree();
        System.out.println("任務三,當前線程:" + Thread.currentThread().getName());
        return new AsyncResult<>("任務三完成");
    }
}

在 單元測試 用例中,注入 AsyncExecutorTask 對象,并在測試用例中執(zhí)行 doTaskOne(),doTaskTwo(),doTaskThree() 三個方法。

@SpringBootTest
public class AsyncExecutorTaskTest {
    @Autowired
    private AsyncExecutorTask task;

    @Test
    public void testAsyncExecutorTask() throws Exception {
        task.doTaskOneCallback();
        task.doTaskTwoCallback();
        task.doTaskThreeCallback();

        sleep(30 * 1000L);
    }
}

執(zhí)行一下上述的 單元測試,可以看到如下結(jié)果:

開始做任務一
開始做任務三
開始做任務二
完成任務二,耗時:3905毫秒
任務二,當前線程:taskExecutor-2
完成任務一,耗時:6184毫秒
任務一,當前線程:taskExecutor-1
完成任務三,耗時:9737毫秒
任務三,當前線程:taskExecutor-3

執(zhí)行上面的單元測試,觀察到 任務線程池 的 線程池名的前綴 被打印,說明 線程池 成功執(zhí)行 異步任務!

三、優(yōu)雅地關(guān)閉線程池

由于在應用關(guān)閉的時候異步任務還在執(zhí)行,導致類似 數(shù)據(jù)庫連接池 這樣的對象一并被 銷毀了,當 異步任務 中對 數(shù)據(jù)庫 進行操作就會出錯。

解決方案如下,重新設(shè)置線程池配置對象,新增線程池 setWaitForTasksToCompleteOnShutdown() 和 setAwaitTerminationSeconds() 配置:

@Bean("taskExecutor")
public Executor taskExecutor() {
    ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
    executor.setPoolSize(20);
    executor.setThreadNamePrefix("taskExecutor-");
    executor.setWaitForTasksToCompleteOnShutdown(true);
    executor.setAwaitTerminationSeconds(60);
    return executor;
}
  • setWaitForTasksToCompleteOnShutdown(true): 該方法用來設(shè)置 線程池關(guān)閉 的時候 等待 所有任務都完成后,再繼續(xù) 銷毀 其他的 Bean,這樣這些 異步任務 的 銷毀 就會先于 數(shù)據(jù)庫連接池對象 的銷毀。

  • setAwaitTerminationSeconds(60): 該方法用來設(shè)置線程池中 任務的等待時間,如果超過這個時間還沒有銷毀就 強制銷毀,以確保應用最后能夠被關(guān)閉,而不是阻塞住。

異步任務** 的 銷毀 就會先于 數(shù)據(jù)庫連接池對象 的銷毀。

  • setAwaitTerminationSeconds(60): 該方法用來設(shè)置線程池中 任務的等待時間,如果超過這個時間還沒有銷毀就 強制銷毀,以確保應用最后能夠被關(guān)閉,而不是阻塞住。

到此,相信大家對“springboot為異步任務規(guī)劃自定義線程池如何實現(xiàn)”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!

向AI問一下細節(jié)

免責聲明:本站發(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