Java中的ScheduledThreadPoolExecutor定時(shí)任務(wù)詳解
ScheduledThreadPoolExecutor詳解
ScheduledThreadPoolExecutor 繼承自 ThreadPoolExecutor。它主要用來(lái)在給定的延遲之后運(yùn)行任務(wù),或者定期執(zhí)行任務(wù)。
ScheduledThreadPoolExecutor 的功能與 Timer 類似, 但 ScheduledThreadPoolExecutor 功能更強(qiáng)大、更靈活。
Timer 對(duì)應(yīng)的是單個(gè)后臺(tái)線程,而 ScheduledThreadPoolExecutor 可以在構(gòu)造函數(shù)中指定多個(gè)對(duì)應(yīng)的后臺(tái)線程數(shù)。
ScheduledThreadPoolExecutor 的運(yùn)行機(jī)制

ScheduledThreadPoolExecutor 的任務(wù)傳遞示意圖
ScheduledThreadPoolExecutor 的執(zhí)行主要分為兩大部分:
1) 當(dāng)調(diào)用 ScheduledThreadPoolExecutor 的 scheduleAtFixedRate()方法或者 scheduleWithFixedDelay()方法時(shí),會(huì)向 ScheduledThreadPoolExecutor 的 DelayQueue 添加一個(gè)實(shí)現(xiàn)了 RunnableScheduledFutur 接口的 ScheduledFutureTask。
2) 線程池中的線程從 DelayQueue 中獲取 ScheduledFutureTask,然后執(zhí)行任務(wù)。
ScheduledThreadPoolExecutor 為了實(shí)現(xiàn)周期性的執(zhí)行任務(wù),對(duì) ThreadPoolExecutor 做 了如下的修改:
- 使用 DelayQueue 作為任務(wù)隊(duì)列。
- 獲取任務(wù)的方式不同。
- 執(zhí)行周期任務(wù)后,增加了額外的處理。
ScheduledThreadPoolExecutor 的實(shí)現(xiàn)
ScheduledThreadPoolExecutor 會(huì)把待調(diào)度的任務(wù) (ScheduledFutureTask)放到一個(gè) DelayQueue 中。
ScheduledFutureTask 主要包含 3 個(gè)成員變量,如下。
- long 型成員變量 time,表示這個(gè)任務(wù)將要被執(zhí)行的具體時(shí)間。
- long 型成員變量 sequenceNumber,表示這個(gè)任務(wù)被添加到 ScheduledThreadPoolExecutor 中的序號(hào)。
- long 型成員變量 period,表示任務(wù)執(zhí)行的間隔周期。
DelayQueue 封裝了一個(gè) PriorityQueue,這個(gè) PriorityQueue 會(huì)對(duì)隊(duì)列中的 ScheduledFutureTask 進(jìn)行排序。排序時(shí),time 小的排在前面(時(shí)間早的任務(wù)將被先執(zhí)行)。
如果兩個(gè) ScheduledFutureTask 的 time 相同,就比較 sequenceNumber,sequenceNumber 小的排在前面(也就是說(shuō),如果兩個(gè)任務(wù)的執(zhí)行時(shí)間相同,那么先提交的任務(wù)將被先執(zhí)行)。

ScheduledThreadPoolExecutor 中的線程執(zhí)行周期任務(wù)的過(guò)程
線程 1 從 DelayQueue 中獲取已到期的 ScheduledFutureTask(DelayQueue.take())。
到 期任務(wù)是指 ScheduledFutureTask 的 time 大于等于當(dāng)前時(shí)間。線程 1 執(zhí)行這個(gè) ScheduledFutureTask。
線程 1 修改 ScheduledFutureTask 的 time 變量為下次將要被執(zhí)行的時(shí)間。
線程 1 把這個(gè)修改 time 之后的 ScheduledFutureTask 放回 DelayQueue 中(DelayQueue.add())。
DelayQueue.take()方法
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly(); // 1 獲取響應(yīng)打斷的鎖
try {
for (; ; ) {
E first = q.peek(); // 獲取ScheduledFutureTask
if (first == null) {
available.await(); // 2.1 到 Condition 中等待
} else {
long delay = first.getDelay(TimeUnit.NANOSECONDS);
if (delay > 0) {
// 2.2 到 Condition 中等待到 time 時(shí)間
long tl = available.awaitNanos(delay);
} else {
E x = q.poll(); // 2.3.1 獲取 PriorityQueue 的頭元素
assert x != null;
if (q.size() != 0)
available.signalAll(); // 2.3.2 喚醒在 Condition 中等待的所有線程
return x;
}
}
}
} finally {
lock.unlock(); // 3 釋放??
}
}
1) 獲取 Lock。
2) 獲取周期任務(wù)。
- 如果 PriorityQueue 為空,當(dāng)前線程到 Condition 中等待;否則執(zhí)行下面的 2.2。
- 如果 PriorityQueue 的頭元素的 time 時(shí)間比當(dāng)前時(shí)間大,到 Condition 中等待到 time 時(shí)間;否則執(zhí)行下面的 2.3。
- 獲取 PriorityQueue 的頭元素(2.3.1);如果 PriorityQueue 不為空,則喚醒在 Condition 中等待的所有線程(2.3.2)。
3) 釋放 Lock。
ScheduledThreadPoolExecutor 在一個(gè)循環(huán)中執(zhí)行步驟 2,直到線程從 PriorityQueue 獲 取到一個(gè)元素之后(執(zhí)行 2.3.1 之后),才會(huì)退出無(wú)限循環(huán)(結(jié)束步驟 2)。
DelayQueue.offer()方法
// 把 ScheduledFutureTask 放入 DelayQueue 中的過(guò)程
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock(); // 1
try {
E first = q.peek();
q.offer(e); // 2.1
if (first == null || e.compareTo(first) < 0) available.signalAll(); // 2.2
return true;
} finally {
lock.unlock(); // 3
}
}1) 獲取 Lock。
2) 添加任務(wù)。
- 向 PriorityQueue 添加任務(wù)。
- 如果在上面 2.1 中添加的任務(wù)是 PriorityQueue 的頭元素,喚醒在 Condition 中等 待的所有線程。
3) 釋放 Lock
到此這篇關(guān)于Java中的ScheduledThreadPoolExecutor定時(shí)任務(wù)詳解的文章就介紹到這了,更多相關(guān)ScheduledThreadPoolExecutor詳解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
調(diào)用Process.waitfor導(dǎo)致的進(jìn)程掛起問(wèn)題及解決
這篇文章主要介紹了調(diào)用Process.waitfor導(dǎo)致的進(jìn)程掛起問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
基于Spring接口集成Caffeine+Redis兩級(jí)緩存
這篇文章主要介紹了基于Spring接口集成Caffeine+Redis兩級(jí)緩存,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-07-07
Springboot+Spring Security實(shí)現(xiàn)前后端分離登錄認(rèn)證及權(quán)限控制的示例代碼
本文主要介紹了Springboot+Spring Security實(shí)現(xiàn)前后端分離登錄認(rèn)證及權(quán)限控制的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
java并發(fā)編程專題(四)----淺談(JUC)Lock鎖
這篇文章主要介紹了java并發(fā)編程(JUC)Lock鎖的相關(guān)內(nèi)容,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06
Springboot Druid 自定義加密數(shù)據(jù)庫(kù)密碼的幾種方案
這篇文章主要介紹了Springboot Druid 自定義加密數(shù)據(jù)庫(kù)密碼的步驟,幫助大家更好的理解和使用springboot,感興趣的朋友可以了解下2020-12-12
spring boot devtools在Idea中實(shí)現(xiàn)熱部署方法
這篇文章主要介紹了spring boot devtools在Idea中實(shí)現(xiàn)熱部署方法及注意要點(diǎn),需要的朋友可以參考下2018-02-02
顯示IntelliJ IDEA工具的Run Dashboard功能圖文詳解
這篇文章主要介紹了顯示IntelliJ IDEA工具的Run Dashboard功能,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07

