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 的 scheduleAtFixedRate() 方法或者scheduleWirhFixedDelay() 方法時,會向 ScheduledThreadPoolExecutor 的 DelayQueue 添加一個實現了 RunnableScheduledFuture 接口的 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)