任務調度
定義:任務調度是基於給定時間點,給定時間間隔或者給定執行次數自動執行任務。java
Timer
Timer 是jdk提供的一個定時器工具,會在主線程以外起一個單獨線程執行指定的計劃任務。數組
public class TimerTest extends TimerTask { private String jobName; public TimerTest(String jobName) { this.jobName = jobName; } @Override public void run() { System.out.println("執行:" + jobName); } public static void main(String[] args) { Timer timer = new Timer(); long delay1 = 1 * 1000; long interval1 = 1000; //從如今開始1秒後每隔1秒執行一次job1 timer.schedule(new TimerTest("job1"), delay1, interval1); long delay2 = 2 * 1000; long interval2 = 2000; // 從如今開始2秒鐘以後,每隔2秒鐘執行一次 job2 timer.schedule(new TimerTest("job2"), delay2, interval2); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } //終止執行 timer.cancel(); } }
Timer 的優勢在於簡單易用,但因爲全部任務都是由同一個線程來調度,所以全部任務都是串行執行的,同一時間只能有一個任務在執行,前一個任務的延遲或異常都將會影響到以後的任務併發
ScheduledExecutor
ScheduledExecutor 類則在自定義線程池的基礎上增長了週期性執行任務的功能ide
ScheduledThreadPoolExecutor 線程池中的每個線程負責執行一個定時任務,相互之間互不干擾,併發執行,線程池會輪詢任務狀態,執行時間到來纔會真正執行線程。函數
public class ScheduledExecutorTest implements Runnable { private String jobName; public ScheduledExecutorTest(String jobName) { this.jobName = jobName; } public void run() { System.out.println("Start: " + getDate()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("execute " + jobName); System.out.println("End: " + getDate()); } //獲取當前時間 public String getDate() { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//設置日期格式 return df.format(new Date());// new Date()爲獲取當前系統時間 } public static void main(String[] args) { ScheduledExecutorService service = Executors.newScheduledThreadPool(10); long initialDelay1 = 1; long period1 = 2; //固定時間間隔 service.scheduleAtFixedRate(new ScheduledExecutorTest("job1"), initialDelay1, period1, TimeUnit.SECONDS); long initialDelay2 = 1; long peroid2 = 3; //本次執行結束和下次開始執行時間保持在三秒 //每次執行時間:executeTime + peroid executeTime可能會變化,可是peroid不變 /*service.scheduleWithFixedDelay(new ScheduledExecutorTest("job2"), initialDelay2, peroid2, TimeUnit.SECONDS); */ } }
使用Executors.newScheduledThreadPool(10)建立一個核心線程數量爲10的線程池。工具
方法區別:
1.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
參數:this
- Runnable 須要執行的task
- initialDelay 延遲至啓動任務的時間
- period 本次開始到下次開始的時間間隔
注:spa
- 若本次job執行時間小於period,本次開始執行和下次開始之間時間間隔固定
- 若本次job執行時間大於period,則本次開始時間和下次開始時間間隔爲job執行時間
- 每次執行時間 :executeTime < period ? peroid : executeTime
- 不會由於上一個線程的調度失效延遲而受到影響
2.scheduleWithFixedDelay(Runnable command, long initialDelay, long period, TimeUnit unit)
注:線程
- 本次執行結束和下次開始執行時間保持在三秒
- 每次執行時間:executeTime + peroid executeTime可能會變化,可是peroid不變
- 受到上一個線程調度影響,可能會推遲本次任務調度的執行
實現原理:
ScheduledThreadPool建立的線程池底層使用的是DelayQueuecode
Delayed元素的一個無界阻塞隊列,只有在延遲期滿時才能從中提取元素。該隊列的頭部是延遲期滿後保存時間最長的Delayed元素。若是延遲都尚未期滿,則隊列沒有頭部,而且poll將返回null。
當一個元素的 getDelay(TimeUnit.NANOSECONDS)方法返回一個小於等於0的值時,將發生到期。即便沒法使用take或poll移除未到期的元素,也不會將這些元素做爲正常元素對待。例如,size方法同時返回到期和未到期元素的計數。此隊列不容許使用null元素
ScheduledThreadPoolExecutor是把任務添加到DelayedWorkQueue中,而DelayedWorkQueue則是相似於DelayQueue,內部維護着一個以時間爲前後順序的優先級隊列,使用compareTo()方法使用與DelayedWorkQueue隊列對其元素ScheduledThreadPoolExecutor task進行排序
小結:
上面建立線程池建議使用ScheduledThreadPoolExecutor,能夠更加精確控制。 構造函數其中一個參數爲DelayedWorkQueue,內部經過一個RunnableScheduledFuture數組保存Runnable任務,默認初始長度爲16,它是一個優先級隊列 它根據這個任務的下次執行時間來比較大小,這樣可以保證queue[0]位置上的元素是最近須要執行的任務。