ScheduleThreadPoolExecutor的工作原理與使用示例


歡迎探討,如有錯誤敬請指正

如需轉載,請注明出處  http://www.cnblogs.com/nullzx/


 

1. ScheduleExecutorService接口、ScheduledFuture接口

ScheduleExecutorService ScheduledFuture

從圖中可以看出ScheduledExecutorService接口繼承了ExecutorService接口,同時還添加了有關提交定時任務的四個方法。

public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
//向定時任務線程池提交一個延時Runnable任務(僅執行一次)
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
//向定時任務線程池提交一個延時的Callable任務(僅執行一次)
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, 
                                                  long period, TimeUnit unit)
//向定時任務線程池提交一個固定時間間隔執行的任務
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
                                                      long delay, TimeUnit unit);
//向定時任務線程池提交一個固定延時間隔執行的任務

固定時間間隔的任務不論每次任務花費多少時間,下次任務開始執行時間是確定的,當然執行任務的時間不能超過執行周期。

固定延時間隔的任務是指每次執行完任務以后都延時一個固定的時間。由於操作系統調度以及每次任務執行的語句可能不同,所以每次任務執行所花費的時間是不確定的,也就導致了每次任務的執行周期存在一定的波動。

注意:定時或延時任務中所涉及到時間、周期不能保證實時性及准確性,實際運行中會有一定的誤差。

從上圖可以還可以看出ScheduledThreadPoolExecutor還直接繼承了ThreadPoolExecutor。這樣做是為了利用ThreadPoolExecutor已實現的方法。

可以向定時任務線程池提交普通任務。對於定時任務線程池而言,普通任務只不過是延時執行時間為0,周期為0的任務。

從上述四個方法中的返回值可以看出,當向線程池提交任務時會返回一個ScheduleFuture接口的對象。通過下圖可以看出,ScheduledFuture接口繼承了Delayed和Future接口。我們可以通過ScheduleFutured對象的cancel方法可以結束一個定時任務。

 

SFT

在ScheduledThreadPoolExecutor中阻塞隊列存儲的是ScheduledFutureTask對象,它是任務真正的載體。但是ScheduledFutureTask對象同樣可以看做Runnable對象,所以同樣可以使用ThreadPoolExecutor中的方法來處理它。

2. ScheduledThreadPoolExecutor的運行原理

定時任務線程池工作原理

在圖中,我們將ScheduleFutureTask簡稱為SFT

在定時任務線程池中存儲任務的隊列是具有優先性質的阻塞隊列。在這個隊列中離下次執行時間最近的任務位於隊首(關於優先隊列的原理請參照本博客后續數據結構的相關內容)。線程每次通過優先隊列的take方法獲取任務。如果隊列為空take方法阻塞,否則take方法從隊首獲取帶執行的任務(即ScheduleFutureTask對象),然后從任務的getDelay方法獲取應當延時的時間,再通過帶參數的await方法進行延時。當延時時間已到,則開始執行任務(在執行之前還要檢測這個任務是否已被取消),執行完畢以后繼續判斷這是否是一個周期任務。如果不是,調用take方法獲取新任務,重復上述操作。如果是計算下一次執行的時間,然后調用隊列的add方法將這個任務再次入列。入列完成后,繼續調用take方法獲取新任務,這樣重復的執行下去。

以上就是定時任務線程池工作的最基本原理,實際上take、add、任務的取消等過程比較復雜,圖中也沒有說明線程在執行await方法時隊首元素改變時的情況。如果想繼續了解,請參閱本博客“ScheduledThreadPoolExecutor源代碼分析”的文章。

3. ScheduleThreadPoolExecutor與Timer相比的優勢。

(1)Timer是基於絕對時間的延時執行或周期執行,當系統時間改變,則任務的執行會受到的影響。而ScheduleThreadPoolExecutore中,任務時基於相對時間進行周期或延時操作。

(2)Timer也可以提交多個TimeTask任務,但只有一個線程來執行所有的TimeTask,這樣並發性受到影響。而ScheduleThreadPoolExecutore可以設定池中線程的數量。

(3)Timer不會捕獲TimerTask的異常,只是簡單地停止,這樣勢必會影響其他TimeTask的執行。而ScheduleThreadPoolExecutore中,如果一個線程因某些原因停止,線程池可以自動創建新的線程來維護池中線程的數量。

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;
		}
		@Override
		public 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();
	}
}

 在這個示例中,定義了一個內部類TimerTask,它實現了Runnable接口。在main方法中創建了一個定時任務線程池,向它提交了四個任務a,b,c,d。四個任務起始的延時時間分別是200ms、400ms、600ms、800ms,執行周期都為1000ms。5秒以后取消了定時任務a的執行。又過了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. 參考內容

[1] http://janeky.iteye.com/blog/770441


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM