相信大家都已經非常熟悉 java.util.Timer 了,它是最簡單的壹種實現任務調度的方法,下面給出壹個具體的例子:
清單 1. 使用 Timer 進行任務調度
package com.ibm.scheduler;
import java.util.Timer;
import java.util.TimerTask;
public class TimerTest extends TimerTask {
private String jobName = "";
public TimerTest(String jobName) {
super();
this.jobName = jobName;
}
@Override
public void run() {
System.out.println("execute " + jobName);
}
public static void main(String[] args) {
Timer timer = new Timer();
long delay1 = 1 * 1000;
long period1 = 1000;
// 從現在開始 1 秒鐘之後,每隔 1 秒鐘執行壹次 job1
timer.schedule(new TimerTest("job1"), delay1, period1);
long delay2 = 2 * 1000;
long period2 = 2000;
// 從現在開始 2 秒鐘之後,每隔 2 秒鐘執行壹次 job2
timer.schedule(new TimerTest("job2"), delay2, period2);
}
}
Output:
execute job1
execute job1
execute job2
execute job1
execute job1
execute job2
使用 Timer 實現任務調度的核心類是 Timer 和 TimerTask。其中 Timer 負責設定 TimerTask 的起始與間隔執行時間。使用者只需要創建壹個 TimerTask 的繼承類,實現自己的 run 方法,然後將其丟給 Timer 去執行即可。
Timer 的設計核心是壹個 TaskList 和壹個 TaskThread。Timer 將接收到的任務丟到自己的 TaskList 中,TaskList 按照 Task 的最初執行時間進行排序。TimerThread 在創建 Timer 時會啟動成為壹個守護線程。這個線程會輪詢所有任務,找到壹個最近要執行的任務,然後休眠,當到達最近要執行任務的開始時間點,TimerThread 被喚醒並執行該任務。之後 TimerThread 更新最近壹個要執行的任務,繼續休眠。
Timer 的優點在於簡單易用,但由於所有任務都是由同壹個線程來調度,因此所有任務都是串行執行的,同壹時間只能有壹個任務在執行,前壹個任務的延遲或異常都將會影響到之後的任務。
回頁首
ScheduledExecutor
鑒於 Timer 的上述缺陷,Java 5 推出了基於線程池設計的 ScheduledExecutor。其設計思想是,每壹個被調度的任務都會由線程池中壹個線程去執行,因此任務是並發執行的,相互之間不會受到幹擾。需要註意的是,只有當任務的執行時間到來時,ScheduedExecutor 才會真正啟動壹個線程,其余時間 ScheduledExecutor 都是在輪詢任務的狀態。
清單 2. 使用 ScheduledExecutor 進行任務調度
package com.ibm.scheduler;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorTest implements Runnable {
private String jobName = "";
public ScheduledExecutorTest(String jobName) {
super();
this.jobName = jobName;
}
@Override
public void run() {
System.out.println("execute " + jobName);
}
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
long initialDelay1 = 1;
long period1 = 1;
// 從現在開始1秒鐘之後,每隔1秒鐘執行壹次job1
service.scheduleAtFixedRate(
new ScheduledExecutorTest("job1"), initialDelay1,
period1, TimeUnit.SECONDS);
long initialDelay2 = 1;
long delay2 = 1;
// 從現在開始2秒鐘之後,每隔2秒鐘執行壹次job2
service.scheduleWithFixedDelay(
new ScheduledExecutorTest("job2"), initialDelay2,
delay2, TimeUnit.SECONDS);
}
}
Output:
execute job1
execute job1
execute job2
execute job1
execute job1
execute job2
清單 2 展示了 ScheduledExecutorService 中兩種最常用的調度方法 ScheduleAtFixedRate 和 ScheduleWithFixedDelay。ScheduleAtFixedRate 每次執行時間為上壹次任務開始起向後推壹個時間間隔,即每次執行時間為 :initialDelay, initialDelay+period, initialDelay+2*period, …;ScheduleWithFixedDelay 每次執行時間為上壹次任務結束起向後推壹個時間間隔,即每次執行時間為:initialDelay, initialDelay+executeTime+delay, initialDelay+2*executeTime+2*delay。由此可見,ScheduleAtFixedRate 是基於固定時間間隔進行任務調度,ScheduleWithFixedDelay 取決於每次任務執行的時間長短,是基於不固定時間間隔進行任務調度。
回頁首
用 ScheduledExecutor 和 Calendar 實現復雜任務調度
Timer 和 ScheduledExecutor 都僅能提供基於開始時間與重復間隔的任務調度,不能勝任更加復雜的調度需求。比如,設置每星期二的 16:38:10 執行任務。該功能使用 Timer 和 ScheduledExecutor 都不能直接實現,但我們可以借助 Calendar 間接實現該功能。
清單 3. 使用 ScheduledExcetuor 和 Calendar 進行任務調度
package com.ibm.scheduler;
import java.util.Calendar;
import java.util.Date;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExceutorTest2 extends TimerTask {
private String jobName = "";
public ScheduledExceutorTest2(String jobName) {
super();
this.jobName = jobName;
}
@Override
public void run() {
System.out.println("Date = "+new Date()+", execute " + jobName);
}
/**
* 計算從當前時間currentDate開始,滿足條件dayOfWeek, hourOfDay,
* minuteOfHour, secondOfMinite的最近時間
* @return
*/
public Calendar getEarliestDate(Calendar currentDate, int dayOfWeek,
int hourOfDay, int minuteOfHour, int secondOfMinite) {
//計算當前時間的WEEK_OF_YEAR,DAY_OF_WEEK, HOUR_OF_DAY, MINUTE,SECOND等各個字段值
int currentWeekOfYear = currentDate.get(Calendar.WEEK_OF_YEAR);
int currentDayOfWeek = currentDate.get(Calendar.DAY_OF_WEEK);
int currentHour = currentDate.get(Calendar.HOUR_OF_DAY);
int currentMinute = currentDate.get(Calendar.MINUTE);
int currentSecond = currentDate.get(Calendar.SECOND);
//如果輸入條件中的dayOfWeek小於當前日期的dayOfWeek,則WEEK_OF_YEAR需要推遲壹周
boolean weekLater = false;
if (dayOfWeek < currentDayOfWeek) {
weekLater = true;
} else if (dayOfWeek == currentDayOfWeek) {
//當輸入條件與當前日期的dayOfWeek相等時,如果輸入條件中的
//hourOfDay小於當前日期的
//currentHour,則WEEK_OF_YEAR需要推遲壹周
if (hourOfDay < currentHour) {
weekLater = true;
} else if (hourOfDay == currentHour) {
//當輸入條件與當前日期的dayOfWeek, hourOfDay相等時,
//如果輸入條件中的minuteOfHour小於當前日期的
//currentMinute,則WEEK_OF_YEAR需要推遲壹周
if (minuteOfHour < currentMinute) {
weekLater = true;
} else if (minuteOfHour == currentSecond) {
//當輸入條件與當前日期的dayOfWeek, hourOfDay,
//minuteOfHour相等時,如果輸入條件中的
//secondOfMinite小於當前日期的currentSecond,
//則WEEK_OF_YEAR需要推遲壹周
if (secondOfMinite < currentSecond) {
weekLater = true;
}
}
}
}
if (weekLater) {
//設置當前日期中的WEEK_OF_YEAR為當前周推遲壹周
currentDate.set(Calendar.WEEK_OF_YEAR, currentWeekOfYear + 1);
}
// 設置當前日期中的DAY_OF_WEEK,HOUR_OF_DAY,MINUTE,SECOND為輸入條件中的值。
currentDate.set(Calendar.DAY_OF_WEEK, dayOfWeek);
currentDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
currentDate.set(Calendar.MINUTE, minuteOfHour);
currentDate.set(Calendar.SECOND, secondOfMinite);
return currentDate;
}
public static void main(String[] args) throws Exception {
ScheduledExceutorTest2 test = new ScheduledExceutorTest2("job1");
//獲取當前時間
Calendar currentDate = Calendar.getInstance();
long currentDateLong = currentDate.getTime().getTime();
System.out.println("Current Date = " + currentDate.getTime().toString());
//計算滿足條件的最近壹次執行時間
Calendar earliestDate = test
.getEarliestDate(currentDate, 3, 16, 38, 10);
long earliestDateLong = earliestDate.getTime().getTime();
System.out.println("Earliest Date = "
+ earliestDate.getTime().toString());
//計算從當前時間到最近壹次執行時間的時間間隔
long delay = earliestDateLong - currentDateLong;
//計算執行周期為壹星期
long period = 7 * 24 * 60 * 60 * 1000;
ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
//從現在開始delay毫秒之後,每隔壹星期執行壹次job1
service.scheduleAtFixedRate(test, delay, period,
TimeUnit.MILLISECONDS);
}
}
Output:
Current Date = Wed Feb 02 17:32:01 CST 2011
Earliest Date = Tue Feb 8 16:38:10 CST 2011
Date = Tue Feb 8 16:38:10 CST 2011, execute job1
Date = Tue Feb 15 16:38:10 CST 2011, execute job1