首頁 > 軟體

springboot為非同步任務規劃自定義執行緒池的實現

2022-06-14 10:01:27

一、Spring Boot任務執行緒池

執行緒池的作用

  • 防止資源佔用無限的擴張
  • 呼叫過程省去資源的建立和銷燬所佔用的時間

在高並行環境下,不斷的分配新資源,可能導致系統資源耗盡。所以為了避免這個問題,我們為非同步任務規劃一個執行緒池。當然,如果沒有設定執行緒池的話,springboot會自動設定一個ThreadPoolTaskExecutor 執行緒池到bean當中。

# 核心執行緒數
spring.task.execution.pool.core-size=8  
# 最大執行緒數
spring.task.execution.pool.max-size=16
# 空閒執行緒存活時間
spring.task.execution.pool.keep-alive=60s
# 是否允許核心執行緒超時
spring.task.execution.pool.allow-core-thread-timeout=true
# 執行緒佇列數量
spring.task.execution.pool.queue-capacity=100
# 執行緒關閉等待
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
# 執行緒名稱字首
spring.task.execution.thread-name-prefix=task-

在springboot組態檔中加入上面的設定,即可實現ThreadPoolTaskExecutor 執行緒池。

二、自定義執行緒池

有的時候,我們希望將系統內的一類任務放到一個執行緒池,另一類任務放到另外一個執行緒池,所以使用Spring Boot自帶的任務執行緒池就捉襟見肘了。下面介紹自定義執行緒池的方法。

建立一個 執行緒池設定類 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;
    }
}

上面我們通過使用 ThreadPoolTaskExecutor 建立了一個 執行緒池,同時設定了以下這些引數:

執行緒池屬性屬性的作用上文程式碼設定初始值
核心執行緒數CorePoolSize執行緒池建立時候初始化的執行緒數,最小執行緒數10
最大執行緒數MaxPoolSize執行緒池最大的執行緒數,只有在緩衝佇列滿了之後,才會申請超過核心執行緒數的執行緒20
緩衝任務佇列QueueCapacity用來緩衝執行任務的佇列200
允許執行緒的空閒時間KeepAliveSeconds超過了核心執行緒之外的執行緒,在空閒時間到達之後,沒活幹的執行緒會被銷燬60秒
執行緒池名的字首 ThreadNamePrefix可以用於定位處理任務所在的執行緒池taskExecutor-
執行緒池對任務的Reject策略RejectedExecutionHandler當執行緒池執行飽和,或者執行緒池處於shutdown臨界狀態時,用來拒絕一個任務的執行CallerRunsPolicy

Reject策略預定義有四種:

  • AbortPolicy,用於被拒絕任務的處理程式,它將丟擲RejectedExecutionException。
  • CallerRunsPolicy,用於被拒絕任務的處理程式,它直接在execute方法的呼叫執行緒中執行被拒絕的任務。
  • DiscardOldestPolicy,用於被拒絕任務的處理程式,它放棄最舊的未處理請求,然後重試execute。
  • DiscardPolicy,用於被拒絕任務的處理程式,預設情況下它將丟棄被拒絕的任務。

建立 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 物件,並在測試用例中執行 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);
    }
}

執行一下上述的 單元測試,可以看到如下結果:

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

執行上面的單元測試,觀察到 任務執行緒池 的 執行緒池名的字首 被列印,說明 執行緒池 成功執行 非同步任務!

三、優雅地關閉執行緒池

由於在應用關閉的時候非同步任務還在執行,導致類似 資料庫連線池 這樣的物件一併被 銷燬了,當 非同步任務 中對 資料庫 進行操作就會出錯。

解決方案如下,重新設定執行緒池設定物件,新增執行緒池 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): 該方法用來設定 執行緒池關閉 的時候 等待 所有任務都完成後,再繼續 銷燬 其他的 Bean,這樣這些 非同步任務 的 銷燬 就會先於 資料庫連線池物件 的銷燬。
  • setAwaitTerminationSeconds(60): 該方法用來設定執行緒池中 任務的等待時間,如果超過這個時間還沒有銷燬就 強制銷燬,以確保應用最後能夠被關閉,而不是阻塞住。

非同步任務** 的 銷燬 就會先於 資料庫連線池物件 的銷燬。

  • setAwaitTerminationSeconds(60): 該方法用來設定執行緒池中 任務的等待時間,如果超過這個時間還沒有銷燬就 強制銷燬,以確保應用最後能夠被關閉,而不是阻塞住。

 到此這篇關於springboot為非同步任務規劃自定義執行緒池的實現的文章就介紹到這了,更多相關springboot非同步自定義執行緒池內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com