JUC源碼分析-ScheduledThreadPoolExecutor

概述

ScheduledThreadPoolExecutor 預定任務線程池,用於執行延遲和週期性任務。

 

 

核心屬性和數據結構

//是否應該廢棄週期任務 當關閉時

private volatile boolean continueExistingPeriodicTasksAfterShutdown;

//是否應該取消非週期任務 當關閉時

private volatile boolean executeExistingDelayedTasksAfterShutdown = true;

主要類圖

ScheduledThreadPoolExecutor繼承ThreadPoolExecutor實現了ScheduledExecutorService接口, 依賴DelayedWorkQueue實現延遲和排序,依賴ScheduledFutureTask實現週期性執行任務。

 

源碼分析

ScheduledThreadPoolExecutor構造方法分析

常用的創建ScheduledThreadPoolExecutor的寫法如下。

ScheduledExecutorService service=Executors.newScheduledThreadPool(3,new ThreadFactory() {

@Override

public Thread newThread(Runnable r) {

// TODO Auto-generated method stub

return new Thread(r,"ScheduledThreadPoolExecutorTest");

}

});

跟蹤代碼

public static ScheduledExecutorService newScheduledThreadPool(

int corePoolSize, ThreadFactory threadFactory) {

return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);

}

設置一些默認參數

public ScheduledThreadPoolExecutor(int corePoolSize,

ThreadFactory threadFactory) {

super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,

new DelayedWorkQueue(), threadFactory);

}

 

 

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory) {

this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,

threadFactory, defaultHandler);//新增defaultHandler,它是默認的拒絕策略實現

}

 

private static final RejectedExecutionHandler defaultHandler =

new AbortPolicy();

調用父類構造方法,就是ThreadPoolExecutor 前面文章有分析,這裏不詳細講解了。

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler) {

if (corePoolSize < 0 ||

maximumPoolSize <= 0 ||

maximumPoolSize < corePoolSize ||

keepAliveTime < 0)

throw new IllegalArgumentException();

if (workQueue == null || threadFactory == null || handler == null)

throw new NullPointerException();

this.corePoolSize = corePoolSize;

this.maximumPoolSize = maximumPoolSize;

this.workQueue = workQueue;

this.keepAliveTime = unit.toNanos(keepAliveTime);

this.threadFactory = threadFactory;

this.handler = handler;

}

注意到這裏 corePoolSize==3,maximumPoolSize==Integer.MAX_VALUE,keepAliveTime==0,unit==TimeUnit.NANOSECONDS

workQueue== new DelayedWorkQueue()。

 

ScheduledExecutorService 常用方法如下

public interface ScheduledExecutorService extends ExecutorService {

//延遲delay 單位unit 執行command

public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);

//延遲delay 單位unit 執行callable

public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);

//延遲delay 單位unit, 以period 單位unit 爲週期執行command

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);

 

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);

 

}

 

以沒有周期,延遲執行的schedule方法爲例

 

public ScheduledFuture<?> schedule(Runnable command,

long delay,

TimeUnit unit) {

if (command == null || unit == null)

throw new NullPointerException();

RunnableScheduledFuture<?> t = decorateTask(command,

new ScheduledFutureTask<Void>(command, null,

triggerTime(delay, unit)));

delayedExecute(t);

return t;

}

 

protected <V> RunnableScheduledFuture<V> decorateTask(

Runnable runnable, RunnableScheduledFuture<V> task) {

return task;

}

實際上返回了 new ScheduledFutureTask<Void>(command, null,triggerTime(delay, unit))

triggerTime 根據延遲時間計算出觸發時間

private long triggerTime(long delay, TimeUnit unit) {

return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));

}

 

long triggerTime(long delay) {

return now() +

((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));

}

ScheduledFutureTask構造方法

ScheduledFutureTask(Runnable r, V result, long ns) {

super(r, result);

this.time = ns;//觸發時間

this.period = 0;//觸發週期等於0

this.sequenceNumber = sequencer.getAndIncrement();//

}

調用父類構造方法

public FutureTask(Runnable runnable, V result) {

sync = new Sync(Executors.callable(runnable, result));

}

任務執行分析

 

private void delayedExecute(RunnableScheduledFuture<?> task) {

if (isShutdown())//如果關閉 執行拒絕策略

reject(task);

else {

super.getQueue().add(task);//重要

if (isShutdown() && 線程池關閉

!canRunInCurrentRunState(task.isPeriodic()) &&//不可以運行

remove(task))//刪除任務成功

task.cancel(false);執行取消

else

ensurePrestart();//啓動線程

}

}

boolean canRunInCurrentRunState(boolean periodic) {

return isRunningOrShutdown(periodic ?//根據是否是週期任務 判斷 SHUTDOWN 狀態是否允許運行continueExistingPeriodicTasksAfterShutdown :// 是否執行還存在的週期任務 默認false

executeExistingDelayedTasksAfterShutdown);//是否執行還存在的延遲任務 默認true

}

默認情況下, 週期任務 shutdownOK==false,非週期任務shutdownOK==true

當前非週期,shutdownOK==true .

void ensurePrestart() {

int wc = workerCountOf(ctl.get());

if (wc < corePoolSize) //工作線程數小於corePoolSize,啓動工作線程

addWorker(null, true);

else if (wc == 0) //如果corePoolSize 至少啓動一個工作線程

addWorker(null, false);

}

super.getQueue().add(task)分析

前面寫到 workerQueue的類型是DelayedWorkQueue。

DelayedWorkQueue添加數據方法分析

public boolean add(Runnable e) {

return offer(e);

}

public boolean offer(Runnable x) {

if (x == null)

throw new NullPointerException();

RunnableScheduledFuture e = (RunnableScheduledFuture)x;

final ReentrantLock lock = this.lock;

lock.lock();

try {

int i = size;

if (i >= queue.length)

grow();//容量不夠擴容

size = i + 1;

if (i == 0) {

queue[0] = e;

setIndex(e, 0);

} else {

siftUp(i, e);

}

if (queue[0] == e) {//隊列不爲空了 喚醒阻塞

leader = null;

available.signal();

}

} finally {

lock.unlock();

}

return true;

}

這個方法中只有siftUp不好理解,重點分析。

private void siftUp(int k, RunnableScheduledFuture key) {

while (k > 0) {//通過構建最小二叉堆排序

int parent = (k - 1) >>> 1;

RunnableScheduledFuture e = queue[parent];

if (key.compareTo(e) >= 0)

break;

queue[k] = e;

setIndex(e, k);

k = parent;

}

queue[k] = key;

setIndex(key, k);

}

siftUp與PriorityBlockingQueue 添加數據實例中的siftUpComparable處理邏輯相似。都是構建最小二叉堆。

與PriorityBlockingQueue 隊列相同DelayedWorkQueue的獲取方法也有相應的siftDown方法

DelayedWorkQueue獲取方法分析

public RunnableScheduledFuture take() throws InterruptedException {

final ReentrantLock lock = this.lock;

lock.lockInterruptibly();

try {

for (;;) {//循環直到獲取到數據

RunnableScheduledFuture first = queue[0];

if (first == null)//如果隊列爲空阻塞

available.await();

else {

//計算距離當前時間需要延遲的時間值

long delay = first.getDelay(TimeUnit.NANOSECONDS);

if (delay <= 0)//不需要延遲 直接獲取

return finishPoll(first);

else if (leader != null) // 如果其他線程進入 延遲等待,等待直到被喚醒

available.await();//code1

else {//需要延遲 等待,有一個線程執行這裏,其他獲取數據線程需要等待

Thread thisThread = Thread.currentThread();

leader = thisThread;//標記執行延遲等待的線程

try {

available.awaitNanos(delay);

} finally {

if (leader == thisThread)

leader = null;//釋放佔據線程

}

}

}

}

} finally {

if (leader == null && queue[0] != null)

available.signal();//喚醒code1 等待的線程

lock.unlock();

}

}

 

finishPoll需要重點分析

 

private RunnableScheduledFuture finishPoll(RunnableScheduledFuture f) {

int s = --size;

RunnableScheduledFuture x = queue[s];

queue[s] = null;

if (s != 0)

siftDown(0, x);

setIndex(f, -1);

return f;

}

看到了DelayedWorkQueue獲取方法的siftDown方法。siftDown方法的實現邏輯與PriorityBlockingQueue的siftDown相似。

 

DelayedWorkQueue與PriorityBlockingQueue 一樣都使用了最小二叉堆,通過添加數據,獲取數據時 構建二叉堆排序。二叉堆的數據結構和排序算法參照PriorityBlockingQueue分析篇。

 

compareTo分析

排序中用到的compareTo需要分析,key的類型是ScheduledFutureTask

public int compareTo(Delayed other) {

if (other == this) // compare zero ONLY if same object

return 0;//同一個實例,相等

if (other instanceof ScheduledFutureTask) {// 根據觸發時間與other比較 。

ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;

long diff = time - x.time;

if (diff < 0)

return -1;

else if (diff > 0)

return 1;

else if (sequenceNumber < x.sequenceNumber)

return -1;//比較序號。先加入的任務序號小。

else

return 1;

}

//獲取需要延遲的時間值比較

long d = (getDelay(TimeUnit.NANOSECONDS) -

other.getDelay(TimeUnit.NANOSECONDS));

return (d == 0) ? 0 : ((d < 0) ? -1 : 1);

}

compareTo 簡略的邏輯就是根據 觸發時間比較當前實例與其他實例的大小,

 

分析到這裏,清楚了加入workerQueue中的數據時間,獲取後是有序的,先獲取觸發時間比較接近的。

ScheduledFutureTask run分析

條件的任務實際上執行的是ScheduledFutureTask的run方法

public void run() {

boolean periodic = isPeriodic();//判斷是否有周期

if (!canRunInCurrentRunState(periodic))//判斷是否應該執行取消

cancel(false);

else if (!periodic)//非週期性 直接執行

ScheduledFutureTask.super.run();

else if (ScheduledFutureTask.super.runAndReset()) {//執行並重置任務狀態

setNextRunTime();//根據時間週期重置下一次觸發時間

reExecutePeriodic(outerTask);//outerTask 等於this, 將當前任務重新投入隊列。

}

}

上面粗體部分是實現週期執行的關鍵。

邏輯總結:

如果應該取消,執行取消。 如果是非週期性任務值直接執行,如果是週期性任務,先執行並重置任務狀態,然後根據時間週期重置下一次觸發時間,最後將當前任務重新投入隊列。

分析到這裏 延遲執行和週期性的執行都清楚是怎麼樣實現的了。

總結:ScheduledThreadPoolExecutor繼承了ThreadPoolExecutor,沒有使用它的線程池框架,自己來實現。邏輯比較簡單 將任務加入優先級隊列DelayedWorkQueue, 然後啓動核心線程數的工作線程。依賴DelayedWorkQueue實現延遲和排序,依賴ScheduledFutureTask實現週期性執行任務,理解DelayedWorkQueue的排序和延遲實現, 週期性任務執行的邏輯是本文的重點。

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章