之前我們剖析過Java中線程池ThreadPoolExecutor的源碼,鏈接爲 https://blog.csdn.net/HappyHeng/article/details/86827324,這一節從源碼層次講一下ScheduledThreadPoolExecutor這個定時線程池是如何執行任務的。
一、ScheduledFutureTask :
在ThreadPoolExecutor中提交任務執行,只需要提交一個execute(Runnable)即可,ThreadPoolExecutor內部的queue也是存儲Runnable即可。但是在定時線程池中,又將可執行的代碼封裝成一個ScheduledFutureTask,此Task中封裝了Runnable的執行策略與執行時間,下面來看一下其成員變量:
private long time; // 此爲下次執行時機的納秒數
private final long period; // 重複任務的週期(以納秒爲單位)。正值表示固定速率的執行。負值表示固定延遲執行。值0表示非重複任務。
private Callable<V> callable; // 內部使用callable來保存傳入的Runnable
下面講一下線程拿到task後,其執行run()方法的解析:
public void run() {
boolean periodic = isPeriodic();
// 當前的狀態是否允許執行
if (!canRunInCurrentRunState(periodic))
cancel(false);
// periodic表示固定時間段執行,若爲!periodic,則表示不按固定時間段執行,只執行一次即可
else if (!periodic)
ScheduledFutureTask.super.run();
// 若爲periodic,則調用父類的runAndReset()執行相關代碼
else if (ScheduledFutureTask.super.runAndReset()) {
// 執行完成後,設置下一次執行的時間,將時間設置到time字段中
setNextRunTime();
// 將task重新插入到queue隊列中
reExecutePeriodic(outerTask);
}
}
二、DelayedWorkQueue:
看完了ScheduledFutureTask之後,我們知道Task分爲只執行一次與固定速率執行的兩種task,那麼定時線程池是如何等待指定時間纔去執行task呢?
我們知道,在ThreadPoolExecutor中,線程池會阻塞在blockQueue中,直到有runnable存入。而ScheduledFutureTask中,是新寫了支持延時執行的Queue來達到定時執行的目的,即爲DelayedWorkQueue,我們看一下其take()方法:
public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
// 在執行之前首先要獲取到鎖
lock.lockInterruptibly();
try {
for (;;) {
// 在queue中,會根據執行時間將task排序,此爲獲取到第一個執行的task
RunnableScheduledFuture<?> first = queue[0];
if (first == null)
// 如果queue中沒有task,則一直等待
available.await();
else {
// 得到第一個執行的task與現在時間的間隔
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
// 若小與等於0,說明需要買上執行,則返回此task
return finishPoll(first);
first = null; // don't retain ref while waiting
// leader爲正在等待的線程
if (leader != null)
// 如果此時有線程在等待,那麼當前線程需要一直等待
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
// 如果未有線程等待,則當前線程等待第一個task的執行時間
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
// 當執行完成時,調用signal()來喚起其它線程
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
上述簡單的分析了ScheduledThreadPoolExecutor的等待與獲取task的過程,以及task的執行過程,如果以後有時間的話,會以圖畫的形式來分析一下ScheduledThreadPoolExecutor的執行過程。