Scheduled 執行緒池實踐

FunTester發表於2024-11-26

在現代 Java 開發中,任務排程是一項不可或缺的功能。無論是定時資料同步、定期清理無效快取,還是實現任務重試,ScheduledThreadPoolExecutor 都是一個強大的工具。本文將從其基本概念、核心方法、應用場景以及最佳化建議等方面,深入探討這個 Java 併發工具。

基本概念

ScheduledThreadPoolExecutor 是 Java 中 java.util.concurrent 包提供的一個強大的執行緒池實現,專用於排程和執行定時任務或週期性任務。它繼承自 ThreadPoolExecutor,並擴充套件了其功能,允許更精確地安排任務的執行時間。

  • 繼承關係:它是 ThreadPoolExecutor 的子類,同時實現了 ScheduledExecutorService 介面。
  • 核心特點:除了普通執行緒池的併發任務管理功能外,它可以精準地排程任務,包括一次性延遲執行和週期性重複執行。

特點總結:

  1. 執行緒複用:透過執行緒池複用機制,避免了執行緒頻繁建立和銷燬的開銷。
  2. 多工併發支援:支援同時排程多個任務。
  3. 定時精度:基於系統時間,排程精確且高效。
  4. 任務中斷機制:能夠在需要時靈活關閉或取消任務。

建立和配置

構造方法

ScheduledThreadPoolExecutor 提供了多種構造方法,最常見的是以下兩種形式:

public ScheduledThreadPoolExecutor(int corePoolSize) {  
    super(corePoolSize, Integer.MAX_VALUE,  
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,  
          new DelayedWorkQueue());  
}

public ScheduledThreadPoolExecutor(int corePoolSize,  
                                   ThreadFactory threadFactory) {  
    super(corePoolSize, Integer.MAX_VALUE,  
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,  
          new DelayedWorkQueue(), threadFactory);  
}
  • 引數說明:
    • corePoolSize:核心執行緒數,決定了同時執行的執行緒數量。
    • 如果需要更多定製化引數,可以透過 ThreadFactory 自定義執行緒的優先順序、命名等。

配置建議

  • 核心執行緒數 (corePoolSize):根據任務複雜度和執行時間確定,通常等於或略大於 CPU 核心數。
  • 拒絕策略:預設策略會丟擲異常,建議結合業務需要設定,比如丟棄舊任務或呼叫方處理策略。
  • 執行緒工廠:透過自定義 ThreadFactory,可以命名執行緒池中的執行緒,方便排查問題。

核心方法

以下是 ScheduledThreadPoolExecutor 的幾個核心方法,它們構成了定時任務排程的主要功能。

一次性延遲任務

schedule(Runnable command, long delay, TimeUnit unit)此方法延遲指定時間後執行任務。例如:

executor.schedule(() -> System.out.println("延遲任務"), 5, TimeUnit.SECONDS);

固定速率任務

scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)固定時間間隔重複執行任務,不受任務執行時間影響。例如:

executor.scheduleAtFixedRate(() -> {
    System.out.println("固定速率執行:" + System.currentTimeMillis());
}, 1, 3, TimeUnit.SECONDS);

固定延遲任務

scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)在每次任務完成後,延遲指定時間再開始下一次執行。例如:

executor.scheduleWithFixedDelay(() -> {
    System.out.println("固定延遲執行:" + System.currentTimeMillis());
}, 1, 2, TimeUnit.SECONDS);

關閉執行緒池

  • shutdown():優雅關閉執行緒池,等待任務完成後退出。
  • shutdownNow():立即中斷所有任務。
  • isShutdown()isTerminated():用於檢查執行緒池的狀態。

注意事項

避免任務阻塞

當任務執行時間過長時,ScheduledThreadPoolExecutor 可能會因為執行緒資源不足而導致任務無法及時排程執行,最終出現執行緒池飽和的情況。以下是應對這種問題的幾種策略:

  • 將耗時的任務分解為多個小任務,併合理安排其執行順序。
  • 為執行緒池設定更大的核心執行緒數,確保能夠容納更多的併發任務。
  • 將耗時任務交給其他執行緒池或非同步框架(如 CompletableFuture),釋放主執行緒池資源。
  • 對任務排程進行限流,確保任務不會超出執行緒池的承載能力。
  • 設定任務的最大執行時間,超時後強制中斷,防止任務佔用執行緒過久。

異常處理

當週期性任務丟擲未捕獲的異常時,ScheduledThreadPoolExecutor 中負責執行該任務的執行緒可能會終止,導致任務無法繼續排程執行。因此,需要為任務新增異常處理邏輯,以確保執行緒的穩定執行。

下面是一個演示例子:

executor.scheduleAtFixedRate(() -> {
    try {
        // 任務邏輯
    } catch (Exception e) {
        System.err.println("任務異常:" + e.getMessage());
    }
}, 0, 1, TimeUnit.SECONDS);

選擇合適的排程方法

scheduleAtFixedRatescheduleWithFixedDelay 這兩種排程方式的主要區別在於任務的執行間隔計算方式,具體表現為是否會受到任務執行時間的影響。

scheduleAtFixedRate

  • 嚴格的固定速率:它以固定的時間間隔排程任務,不考慮任務的實際執行時間。
  • 時間點固定:假設初始延遲為 initialDelay,任務的執行時間點為:
    t0 = initialDelay
    t1 = t0 + period
    t2 = t1 + period,以此類推。

適合任務執行時間較短(小於排程週期),並且對時間間隔有嚴格要求的場景。例如:

  • 定期傳送心跳包。
  • 週期性更新 UI 資料。

scheduleWithFixedDelay

  • 任務完成後才開始計時:它會等待上一個任務完成後,再延遲設定的時間開始執行下一個任務。
  • 動態間隔:執行時間和延遲時間之和決定了任務的執行頻率。

適合任務之間有依賴關係或任務執行時間不確定的場景。例如:

  • 一個資料同步任務,需要等待上一次同步完全完成後,確保下一次同步不會衝突。
  • 網路爬蟲任務,等待上一個爬取任務完成後再延遲執行。

時間線分析

  1. 如果任務執行時間為 3 秒,延遲時間為 2 秒:
    • 第一次任務在 t0=0 秒開始,t1=3 秒結束。
    • 下一次任務從 t2=t1+2=5 秒開始,任務時間間隔為 5 秒。
  2. 如果任務執行時間較短,比如 1 秒:
    • 第一次任務在 t0=0 秒開始,t1=1 秒結束。
    • 下一次任務從 t2=t1+2=3 秒開始,任務時間間隔為 3 秒。

兩種排程方式的對比

特性 scheduleAtFixedRate scheduleWithFixedDelay
執行時間間隔 固定(忽略任務執行時間) 動態(任務完成後再計時)
任務執行時間影響 可能出現堆積或延遲 不會堆積,保持任務之間的順序
適合場景 任務短小、需要嚴格時間間隔的任務 任務間有依賴、執行時間不確定的任務

總結

ScheduledThreadPoolExecutor 是 Java 開發中實現定時任務的強大工具。透過靈活的排程方式和多工管理,它為定時任務的開發帶來了極大的便利。在實際開發中,合理配置執行緒池大小、捕獲異常、選擇合適的排程方法,能夠幫助我們充分發揮其效能優勢。

對於複雜的定時任務,ScheduledThreadPoolExecutor 不僅提供了高效的併發處理能力,還能簡化任務排程邏輯,是 Java 併發程式設計的利器。

FunTester 原創精華
  • 混沌工程、故障測試、Web 前端
  • 服務端功能測試
  • 效能測試專題
  • Java、Groovy、Go
  • 白盒、工具、爬蟲、UI 自動化
  • 理論、感悟、影片
如果覺得我的文章對您有用,請隨意打賞。您的支援將鼓勵我繼續創作!
打賞支援
暫無回覆。

相關文章