簡介
在探討時(shí) ThreadPoolExecutor 只介紹了FixedThreadPool、CachedThreadPool、SingleThreadExecutor,并沒有去介紹ScheduledThreadPoolExecutor,因?yàn)?ScheduledThreadPoolExecutor 與其他線程池的概念有些區(qū)別,它是一個(gè)支持任務(wù)周期性調(diào)度的線程池。
ScheduledThreadPoolExecutor 繼承 ThreadPoolExecutor,同時(shí)通過實(shí)現(xiàn) ScheduledExecutorSerivce 來擴(kuò)展基礎(chǔ)線程池的功能,使其擁有了調(diào)度能力。其整個(gè)調(diào)度的核心在于內(nèi)部類 DelayedWorkQueue ,一個(gè)有序的延時(shí)隊(duì)列。
ScheduledThreadPoolExecutor 的出現(xiàn),很好的彌補(bǔ)了傳統(tǒng) Timer 的不足,具體對比看下表:
Timer | ScheduledThreadPoolExecutor | |
---|---|---|
線程 | 單線程 | 多線程 |
多任務(wù) | 任務(wù)之間相互影響 | 任務(wù)之間不影響 |
調(diào)度時(shí)間 | 絕對時(shí)間 | 相對時(shí)間 |
異常 | 單任務(wù)異常, 后續(xù)任務(wù)受影響 |
無影響 |
構(gòu)造方法
ScheduledThreadPoolExecutor有三個(gè)構(gòu)造形式:
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), handler);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
關(guān)于父類的構(gòu)造可參見 ThreadPoolExecutor。當(dāng)然我們也可以使用工具類Executors的newScheduledThreadPool的方法,快速創(chuàng)建。注意這里使用的DelayedWorkQueue。
ScheduledThreadPoolExecutor沒有提供帶有最大線程數(shù)的構(gòu)造函數(shù)的,默認(rèn)是Integer.MAX_VALUE,說明其可以無限制的開啟任意線程執(zhí)行任務(wù),在大量任務(wù)系統(tǒng),應(yīng)注意這一點(diǎn),避免內(nèi)存溢出。
核心方法
核心方法主要介紹ScheduledThreadPoolExecutor的調(diào)度方法,其他方法與 ThreadPoolExecutor 一致。調(diào)度方法均由 ScheduledExecutorService 接口定義:
public interface ScheduledExecutorService extends ExecutorService {
// 特定時(shí)間延時(shí)后執(zhí)行一次Runnable
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
// 特定時(shí)間延時(shí)后執(zhí)行一次Callable
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
// 固定周期執(zhí)行任務(wù)(與任務(wù)執(zhí)行時(shí)間無關(guān),周期是固定的)
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
// 固定延時(shí)執(zhí)行任務(wù)(與任務(wù)執(zhí)行時(shí)間有關(guān),延時(shí)從上一次任務(wù)完成后開始)
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
代碼中注釋了每個(gè)方法的作用,需注意固定周期與固定延時(shí)的區(qū)別。下面分別對這些方法進(jìn)行測試:
public class ScheduledPoolTest {
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("hh:mm:ss");
private static final Random RANDOM = new Random();
/**
* 輸出:
* 11:04:32
11:04:35
*/
public static void schedule() {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
printTime();
scheduledExecutorService.schedule(new Task(), 3, TimeUnit.SECONDS);
}
/**
* 輸出:
* 11:05:34
11:05:36
11:05:46
11:05:56
11:06:06
11:06:16
......
*/
public static void scheduleAtFixedRate() {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
printTime();
scheduledExecutorService.scheduleAtFixedRate(new Task(), 2, 10, TimeUnit.SECONDS);
}
/**
* 輸出:
* 11:07:39
11:07:41
11:07:54
11:08:08
11:08:22
11:08:33
......
*/
public static void scheduleWithFixedDelay() {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
printTime();
scheduledExecutorService.scheduleWithFixedDelay(new Task(), 2, 10, TimeUnit.SECONDS);
}
static class Task implements Runnable{
public void run() {
printTime();
try {
Thread.sleep(RANDOM.nextInt(5) * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void printTime() {
Date date = new Date();
System.out.println(FORMAT.format(date));
}
}
為了體現(xiàn)scheduleAtFixedRate和scheduleWithFixedDelay的差別,在代碼中我們加入了隨機(jī)睡眠時(shí)間,使任務(wù)執(zhí)行不確定。從注釋中的輸出我們可以看到scheduleAtFixedRate的任務(wù)運(yùn)行周期不受任務(wù)執(zhí)行時(shí)間的影響,而scheduleWithFixedDelay的任務(wù)運(yùn)行周期受任務(wù)執(zhí)行時(shí)間影響較大。
但需注意,如果任務(wù)的執(zhí)行時(shí)間超過任務(wù)調(diào)度周期,比如任務(wù)執(zhí)行需要10s,而給定執(zhí)行時(shí)間間隔是5s的話,任務(wù)的調(diào)度是在任務(wù)10s執(zhí)行完之后立即重新執(zhí)行,而不是5s的周期。
總結(jié)
ScheduledThreadPoolExecutor 在 ThreadPoolExecutor 的基礎(chǔ)上擴(kuò)展了 線程周期調(diào)度功能,使用時(shí)應(yīng)注意控制其調(diào)度的時(shí)間點(diǎn)。
多線程系列目錄(不斷更新中):
線程啟動原理
線程中斷機(jī)制
多線程實(shí)現(xiàn)方式
FutureTask實(shí)現(xiàn)原理
線程池之ThreadPoolExecutor概述
線程池之ThreadPoolExecutor使用
線程池之ThreadPoolExecutor狀態(tài)控制
線程池之ThreadPoolExecutor執(zhí)行原理
線程池之ScheduledThreadPoolExecutor概述
線程池之ScheduledThreadPoolExecutor調(diào)度原理
線程池的優(yōu)雅關(guān)閉實(shí)踐