JAVA多線程(二十三)Java多線程之ScheduledThreadPoolExecutor定時執行任務線程池

1.JAVA多線程(二十三)Java多線程之ScheduledThreadPoolExecutor定時執行任務線程池

1.1 定時執行任務線程池ScheduledThreadPoolExecutor

   ScheduledThreadPoolExecutor是一個使用線程池執行定時任務的類,相較於Java中提供的另一個執行定時任務的類Timer,其主要有如下兩個優點:

  • 使用多線程執行任務,不用擔心任務執行時間過長而導致任務相互阻塞的情況,Timer是單線程執行的,因而會出現這個問題。
  • 不用擔心任務執行過程中,如果線程失活,其會新建線程執行任務,Timer類的單線程掛掉之後是不會重新創建線程執行後續任務的。

時cheduledThreadPoolExecutor定 執行任務線程池繼承結構關係圖
在這裏插入圖片描述

通過源代碼查看ScheduledThreadPoolExecutor構造函數實現:

    /**
     * 創建一個ScheduledThreadPoolExecutor,需要指定核心線程池大小。
     * Creates a new {@code ScheduledThreadPoolExecutor} with the
     * given core pool size.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     */
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

    /**
     * Creates a new ScheduledThreadPoolExecutor with the given
     * initial parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     * @throws NullPointerException if {@code handler} is null
     */
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                       RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), handler);
    }

    /**
     * Creates a new {@code ScheduledThreadPoolExecutor} with the
     * given initial parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     * @throws NullPointerException if {@code threadFactory} is null
     */
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory);
    }


    /**
     * Creates a new ScheduledThreadPoolExecutor with the given
     * initial parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     * @throws NullPointerException if {@code threadFactory} or
     *         {@code handler} is null
     */
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory,
                                       RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory, handler);
    }

在ScheduledThreadPoolExecutor實現中:

  • corePoolSize => 需要自己傳遞參數,指定核心線程數大小。
  • maximumPoolSize => 允許最大的線程數默認Integer.MAX_VALUE無限大。
  • keepAliveTime => keepAliveTime爲0,意味着多餘的空閒線程會被立即終止。
  • workQueue => 採用DelayedWorkQueue作爲線程池的工作隊列。該隊列是一個使用數組實現的優先隊列,在調用ScheduledFutureTask::cancel()方法時,其會根據removeOnCancel變量的設置來確認是否需要將當前任務真正的從隊列中移除,而不只是標識其爲已刪除狀態。

1.2 ScheduledThreadPoolExecutor使用樣例

package com.yuanxw.chapter23;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolExample {
    public static void main(String[] args) {
        // schedule();
        // scheduleAtFixedRate();
        scheduleWithFixedDelay();
    }

    /**
     * 單次定時任務調用
     * schedule(Runnable command, long delay, TimeUnit unit) // 無返回值的延遲任務
     * schedule(Callable callable, long delay, TimeUnit unit) // 有返回值的延遲任務
     */
    private static void schedule() {
        // 設置核心線程數
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2);
        // 創建並執行在給定延遲後啓用的單次操作。
        ScheduledFuture<?> scheduledFuture = scheduledThreadPoolExecutor.schedule(() -> System.out.println("定時任務正在被執行>>>"), 2L, TimeUnit.SECONDS);
        // 嘗試取消執行此任務。
        System.out.println(scheduledFuture.cancel(true));
    }
    /**
         執行結果:
          true
     */      

    /**
     * 固定頻率週期任務
     * 注意:一次任務超時,會持續的影響後續的任務週期;
     */
    private static void scheduleAtFixedRate(){
        // 設置核心線程數
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(5);
        scheduledThreadPoolExecutor.scheduleAtFixedRate(()->System.out.println(String.format("===定時任務線程【%s】正在執行,執行時間:【%s】===", Thread.currentThread().getName(),System.currentTimeMillis())),2,2,TimeUnit.SECONDS);
        /**
         執行結果:
          ===定時任務線程【pool-1-thread-1】正在執行,執行時間:【1580728645370】===
          ===定時任務線程【pool-1-thread-1】正在執行,執行時間:【1580728647372】===
          ===定時任務線程【pool-1-thread-2】正在執行,執行時間:【1580728649372】===
          ===定時任務線程【pool-1-thread-1】正在執行,執行時間:【1580728651374】===
          ===定時任務線程【pool-1-thread-3】正在執行,執行時間:【1580728653371】===
         */
    }

    /**
     * 固定延遲週期任務
     * 注意:固定延遲週期任務,即每次任務結束後,需要再加上等待固定時間;
     */
    private static void scheduleWithFixedDelay(){
        // 設置核心線程數
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(5);
        scheduledThreadPoolExecutor.scheduleWithFixedDelay(() -> {
            System.out.println(String.format("===定時任務線程【%s】正在執行,執行時間:【%s】===", Thread.currentThread().getName(),System.currentTimeMillis()));
            try {
                // 睡眠5秒
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },1,2,TimeUnit.SECONDS);
        /**
         執行結果:
         ===定時任務線程【pool-1-thread-1】正在執行,執行時間:【1580729087258】===
         ===定時任務線程【pool-1-thread-1】正在執行,執行時間:【1580729094268】===
         ===定時任務線程【pool-1-thread-2】正在執行,執行時間:【1580729101272】===
         ===定時任務線程【pool-1-thread-1】正在執行,執行時間:【1580729108273】===
         ===定時任務線程【pool-1-thread-3】正在執行,執行時間:【1580729115275】===
         ===定時任務線程【pool-1-thread-3】正在執行,執行時間:【1580729122277】===
         ===定時任務線程【pool-1-thread-3】正在執行,執行時間:【1580729129279】===
         */
    }
}

1.3 ScheduledThreadPoolExecutor運行機制

   ScheduledThreadPoolExecutor 使用的任務隊列 DelayQueue 封裝了一個 PriorityQueue,PriorityQueue 會對隊列中的任務進行排序,執行所需時間短的放在前面先被執行(ScheduledFutureTask 的 time 變量小的先執行),如果執行所需時間相同則先提交的任務將被先執行(ScheduledFutureTask 的 squenceNumber 變量小的先執行)。
在這裏插入圖片描述

  • ScheduledThreadPoolExecutor 和 Timer 的比較:

    • Timer 對系統時鐘的變化敏感,ScheduledThreadPoolExecutor不是;
    • Timer 只有一個執行線程,因此長時間運行的任務可以延遲其他任務。 ScheduledThreadPoolExecutor 可以配置任意數量的線程。 此外,如果你想(通過提供 ThreadFactory),你可以完全控制創建的線程;
    • 在TimerTask 中拋出的運行時異常會殺死一個線程,從而導致 Timer 死機:-( …即計劃任務將不再運行。ScheduledThreadExecutor 不僅捕獲運行時異常,還允許您在需要時處理它們(通過重寫 afterExecute 方法ThreadPoolExecutor)。拋出異常的任務將被取消,但其他任務將繼續運行。
  • ScheduledThreadPoolExecutor 的執行主要分爲兩大部分:

    • 當調用 ScheduledThreadPoolExecutor 的 scheduleAtFixedRate() 方法或者scheduleWirhFixedDelay() 方法時,會向 ScheduledThreadPoolExecutor 的 DelayQueue 添加一個實現了 RunnableScheduledFuture 接口的 ScheduledFutureTask 。
      線程池中的線程從 DelayQueue 中獲取 ScheduledFutureTask,然後執行任務。
  • ScheduledThreadPoolExecutor 爲了實現週期性的執行任務,對 ThreadPoolExecutor 做了如下修改:

    • 使用 DelayQueue 作爲任務隊列;
    • 獲取任務的方不同
    • 執行週期任務後,增加了額外的處理

1.4 ScheduledThreadPoolExecutor 執行週期任務的步驟

在這裏插入圖片描述

  • 線程 1 從 DelayQueue 中獲取已到期的 ScheduledFutureTask(DelayQueue.take())。到期任務是指 ScheduledFutureTask 的 time 大於等於當前系統的時間;
  • 線程 1 執行這個 ScheduledFutureTask;
  • 線程 1 修改 ScheduledFutureTask 的 time 變量爲下次將要被執行的時間;
  • 線程 1 把這個修改 time 之後的 ScheduledFutureTask 放回 DelayQueue 中(DelayQueue.add())。

    – 以上爲《JAVA多線程(二十三)Java多線程之ScheduledThreadPoolExecutor定時執行任務線程池》,如有不當之處請指出,我後續逐步完善更正,大家共同提高。謝謝大家對我的關注。

——厚積薄發(yuanxw)

發佈了125 篇原創文章 · 獲贊 166 · 訪問量 47萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章