概述
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的排序和延遲實現, 週期性任務執行的邏輯是本文的重點。