線程池之ScheduledThreadPoolExecutor概述

簡介

在探討時(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類圖.png

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í)踐

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容