ScheduleThreadPoolExecutor的工作原理与使用示例
歡迎探討,如有錯誤敬請指正
如需轉載,請注明出處? http://www.cnblogs.com/nullzx/
?
1. ScheduleExecutorService接口、ScheduledFuture接口
從圖中可以看出ScheduledExecutorService接口繼承了ExecutorService接口,同時還添加了有關提交定時任務的四個方法。
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) //向定時任務線程池提交一個延時Runnable任務(僅執(zhí)行一次) public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit); //向定時任務線程池提交一個延時的Callable任務(僅執(zhí)行一次) public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) //向定時任務線程池提交一個固定時間間隔執(zhí)行的任務 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,long delay, TimeUnit unit); //向定時任務線程池提交一個固定延時間隔執(zhí)行的任務固定時間間隔的任務不論每次任務花費多少時間,下次任務開始執(zhí)行時間是確定的,當然執(zhí)行任務的時間不能超過執(zhí)行周期。
固定延時間隔的任務是指每次執(zhí)行完任務以后都延時一個固定的時間。由于操作系統(tǒng)調(diào)度以及每次任務執(zhí)行的語句可能不同,所以每次任務執(zhí)行所花費的時間是不確定的,也就導致了每次任務的執(zhí)行周期存在一定的波動。
注意:定時或延時任務中所涉及到時間、周期不能保證實時性及準確性,實際運行中會有一定的誤差。
從上圖可以還可以看出ScheduledThreadPoolExecutor還直接繼承了ThreadPoolExecutor。這樣做是為了利用ThreadPoolExecutor已實現(xiàn)的方法。
可以向定時任務線程池提交普通任務。對于定時任務線程池而言,普通任務只不過是延時執(zhí)行時間為0,周期為0的任務。
從上述四個方法中的返回值可以看出,當向線程池提交任務時會返回一個ScheduleFuture接口的對象。通過下圖可以看出,ScheduledFuture接口繼承了Delayed和Future接口。我們可以通過ScheduleFutured對象的cancel方法可以結束一個定時任務。
?
在ScheduledThreadPoolExecutor中阻塞隊列存儲的是ScheduledFutureTask對象,它是任務真正的載體。但是ScheduledFutureTask對象同樣可以看做Runnable對象,所以同樣可以使用ThreadPoolExecutor中的方法來處理它。
2. ScheduledThreadPoolExecutor的運行原理
在圖中,我們將ScheduleFutureTask簡稱為SFT
在定時任務線程池中存儲任務的隊列是具有優(yōu)先性質(zhì)的阻塞隊列。在這個隊列中離下次執(zhí)行時間最近的任務位于隊首(關于優(yōu)先隊列的原理請參照本博客后續(xù)數(shù)據(jù)結構的相關內(nèi)容)。線程每次通過優(yōu)先隊列的take方法獲取任務。如果隊列為空take方法阻塞,否則take方法從隊首獲取帶執(zhí)行的任務(即ScheduleFutureTask對象),然后從任務的getDelay方法獲取應當延時的時間,再通過帶參數(shù)的await方法進行延時。當延時時間已到,則開始執(zhí)行任務(在執(zhí)行之前還要檢測這個任務是否已被取消),執(zhí)行完畢以后繼續(xù)判斷這是否是一個周期任務。如果不是,調(diào)用take方法獲取新任務,重復上述操作。如果是計算下一次執(zhí)行的時間,然后調(diào)用隊列的add方法將這個任務再次入列。入列完成后,繼續(xù)調(diào)用take方法獲取新任務,這樣重復的執(zhí)行下去。
以上就是定時任務線程池工作的最基本原理,實際上take、add、任務的取消等過程比較復雜,圖中也沒有說明線程在執(zhí)行await方法時隊首元素改變時的情況。如果想繼續(xù)了解,請參閱本博客“ScheduledThreadPoolExecutor源代碼分析”的文章。
3. ScheduleThreadPoolExecutor與Timer相比的優(yōu)勢。
(1)Timer是基于絕對時間的延時執(zhí)行或周期執(zhí)行,當系統(tǒng)時間改變,則任務的執(zhí)行會受到的影響。而ScheduleThreadPoolExecutore中,任務時基于相對時間進行周期或延時操作。
(2)Timer也可以提交多個TimeTask任務,但只有一個線程來執(zhí)行所有的TimeTask,這樣并發(fā)性受到影響。而ScheduleThreadPoolExecutore可以設定池中線程的數(shù)量。
(3)Timer不會捕獲TimerTask的異常,只是簡單地停止,這樣勢必會影響其他TimeTask的執(zhí)行。而ScheduleThreadPoolExecutore中,如果一個線程因某些原因停止,線程池可以自動創(chuàng)建新的線程來維護池中線程的數(shù)量。
4. 使用示例
package javaleanning;import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit;public class ScheduledThreadPoolExecutorDemo {static class TimerTask implements Runnable{private String id;public TimerTask(String id){this.id = id;}@Overridepublic void run(){System.out.println(id);}}public static void main(String[] args) throws InterruptedException{ScheduledExecutorService ses = Executors.newScheduledThreadPool(2);ScheduledFuture sfa = ses.scheduleAtFixedRate(new TimerTask("a"), 200,1000, TimeUnit.MILLISECONDS);ScheduledFuture sfb = ses.scheduleAtFixedRate(new TimerTask("b"), 400, 1000, TimeUnit.MILLISECONDS);ScheduledFuture sfc = ses.scheduleAtFixedRate(new TimerTask("c"), 600,1000, TimeUnit.MILLISECONDS);ScheduledFuture sfd = ses.scheduleAtFixedRate(new TimerTask("d"), 800, 1000, TimeUnit.MILLISECONDS);Thread.sleep(5000);sfa.cancel(true);Thread.sleep(5000);ses.shutdown();} }?在這個示例中,定義了一個內(nèi)部類TimerTask,它實現(xiàn)了Runnable接口。在main方法中創(chuàng)建了一個定時任務線程池,向它提交了四個任務a,b,c,d。四個任務起始的延時時間分別是200ms、400ms、600ms、800ms,執(zhí)行周期都為1000ms。5秒以后取消了定時任務a的執(zhí)行。又過了5秒,關閉了線程池。默認情況下關閉線程池會結束池所有的任務。
以下是運行結果
a
b
c
d
a
b
c
d
a
b
c
d
a
b
c
d
a
b
c
d
b
c
d
b
c
d
b
c
d
b
c
d
b
c
d
5. 參考內(nèi)容
[1] http://janeky.iteye.com/blog/770441
轉載于:https://www.cnblogs.com/nullzx/p/5188890.html
總結
以上是生活随笔為你收集整理的ScheduleThreadPoolExecutor的工作原理与使用示例的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 队列和栈
- 下一篇: ZOJ 38727(贪心)