1 關於 (時間寶貴的請跳過)
本教程是基於Java定時任務調度工具詳解之Timer篇的學習筆記。
-
什么是定時任務調度
基於給定的時間點
,給定的時間間隔
或者給定的執行次數
自動執行的任務。 -
在Java中的定時調度工具
- Timer
- Quartz
-
兩者主要區別
- 出身上,Timer是Java提供的原生Scheduler(任務調度)工具類,不需要導入其他jar包;而Quartz是OpenSymphony開源組織在任務調度領域的一個開源項目,完全基於Java實現。
- 功能上,如需要實現在某個具體時間執行什么任務的話,Timer足以勝任;如需要實現每個星期六8點鬧鍾提醒之類的復雜任務,就需要用Quartz。因此,Quartz在時間控制上的功能遠比Timer強大和完善。
- 底層上,Timer使用一個后台線程去執行定時任務,Quartz擁有后台線程執行池(類比JDBC連接池),能夠使用多個執行線程去執行定時任務。
簡而言之,Quartz > Timer,Timer是被動地根據既定時間去調度任務的,Quartz則是自己主動定制時間規則去支持更加豐富地調度方法。
本文主要是講解Timer工具類的,故而下文中不會過多討論Quartz。
- 前導知識
Timer, Quartz的使用 | Quartz, Spring的整合 |
---|---|
Java編程知識 | Spring基礎知識 |
2 Timer簡介和配套視頻課程
Timer類位於java.util包下,有關Timer類的詳細描述信息請點擊這里訪問Oracle Java的官方api文檔查閱。
Timer工具類圖是Timer工具類及有關類的類圖。
快速開始:
/*
* Foo.java -- JDK 1.8
*/
package timer;
import java.util.Timer;
import java.util.TimerTask;
/**
* Description:
* <p>
* <p>
* @author ascribed
* @date 2019-04-03 Wed PM 19:15:08
*/
public class Foo {
public static void main(String[] args) {
Timer timer = new Timer(); // 1. 創建Timer實例,關聯線程不能是daemon(守護/后台)線程
FooTimerTask fooTimerTask = new FooTimerTask(); // 2. 創建任務對象
timer.schedule(fooTimerTask, 3000L, 2000L); // 3. 通過Timer定時定頻率調用fooTimerTask的業務代碼
}
}
class FooTimerTask extends TimerTask {
@Override
public void run() {
// TODO 業務代碼......
System.out.println("Hello Timer!");
}
}
3 Timer函數和綜合運用
3.1 Timer定時函數的用法
注意:Timer類在時間granularity(粒度)是毫秒級的,實際上Timer的schedule系列方法獲取時間的方式是System.currentTimeMillis()(當前時間與Unix元年之差,類型為long),最多只能到毫秒級,而一些操作系統的計時精度會達到1/10毫秒。
3.1.1 schedule()方法的4種用法
- schedule(TimerTask task, Date time)
作用
在時間等於或超過time的時候執行且僅執行一次task (單次)。
源碼
public void schedule(TimerTask task, Date time) {
sched(task, time.getTime(), 0);
}
- schedule(TimerTask task, Date firstTime, long period)
作用
時間等於或超過firstTime時首次執行task,之后每隔peroid毫秒重復執行一次task (多次)。
源碼
public void schedule(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, -period);
}
- schedule(TimerTask task, long delay)
作用
等待delay毫秒后執行且僅執行一次task (單次)。
源碼
public void schedule(TimerTask task, long delay) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
sched(task, System.currentTimeMillis()+delay, 0);
}
- schedule(TimerTask task, long delay, long period)
作用
等待delay毫秒后首次執行task, 之后每個period毫秒重復執行一次task (多次)。
源碼
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, period);
}
3.1.2 scheduleAfixRate()的2種用法
- scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
作用
時間等於或超過time時首次執行task,之后每隔peroid毫秒重復執行一次task (多次, 同schedule第2種用法)。
源碼
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, period);
}
- scheduleAtFixedRate(TimerTask task, long delay, long period)
作用
等待delay毫秒后首次執行task, 之后每個period毫秒重復執行一次task (多次, 同schedule第4種用法)。
源碼
public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) {
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, firstTime.getTime(), period);
}
筆記:
- schedule()和scheduleAtFixedRate()方法都能實現對任務的一次或多次調度。
- schedule()按是否可重復執行分為單次和多次,按任務初次執行計算方式分為delay(long型延遲毫秒數)和time(Date型時間)。
- schedule()和scheduleAtFixedRate()最終都是調用Timer類下的sched()方法實現的。
演示代碼包含DemoTimer.java和TimerUtils.java,代碼清單:
/*
* DemoTimer.java -- JDK 1.8
*/
package timer;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
/**
* Description:
* <p>
* <p>
* @author ascribed
* @date 2019-04-03 Wed PM 18:37:03
*/
public class DemoTimer {
public static void main(String[] args) {
Calendar calendar = TimerUtils.current();
TimerUtils.miscellaneous("Current time is : ", calendar.getTime());
calendar.add(Calendar.SECOND, 3); // 當前時間加3秒
Timer timer = new Timer();
DemoTimerTask demoTimerTask = new DemoTimerTask("No. 1");
demoTimerTask.setName("schedule1");
timer.schedule(demoTimerTask, calendar.getTime()); // 3.1.1.1
// demoTimerTask.setName("schedule2");
// timer.schedule(demoTimerTask, calendar.getTime(), 2000); // 3.1.1.2
//
// demoTimerTask.setName("schedule3");
// timer.schedule(demoTimerTask, 3000); // 3.1.1.3
//
// demoTimerTask.setName("schedule4");
// timer.schedule(demoTimerTask, 3000, 2000); // 3.1.1.4
//
// demoTimerTask.setName("scheduleAtFixedRate1");
// timer.scheduleAtFixedRate(demoTimerTask, calendar.getTime(), 2000); // 3.1.2.1
//
// demoTimerTask.setName("scheduleAtFixedRate2");
// timer.scheduleAtFixedRate(demoTimerTask, 3000, 2000); // 3.1.2.2
}
}
class DemoTimerTask extends TimerTask {
String name; // 任務名
public DemoTimerTask(String name) {
this.name = name;
}
@Override
public void run() {
TimerUtils.miscellaneous("Current time is : ", TimerUtils.current().getTime());
System.out.println("Current exec name is : " + name); // 打印當前name的內容
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/*
* TimerUtils.java -- JDK 1.8
*/
package timer;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimerTask;
/**
* Description:
* <p>
* <p>
* @author ascribed
* @date 2019-04-03 Wed PM 18:40:26
*/
public class TimerUtils {
final static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 定義日期格式
static Calendar current() {
Calendar calendar = Calendar.getInstance(); // 通過靜態工廠方法創建Calendar實例
return calendar;
}
static void schedtime(TimerTask task) {
System.out.println("scheduled time is " + sdf.format(task.scheduledExecutionTime()));
}
static void miscellaneous(String str, Date date) {
System.out.println(str + sdf.format(date));
}
}
3.2 其他重要函數
3.2.1 TimerTask的cancel(), scheduledExecutionTime()
- cancel()
作用
終止此計時器,丟棄所有當前已安排(scheduled)的任務。
說明
通過查看源碼,TimerTask的實現機制是通過設置標志位來記錄timer task的狀態,調用cancel()方法的timer task實例並沒有從相應TaskQueue隊列移除,這是和Timer類的cancel()方法不同之處。
源碼
public boolean cancel() {
synchronized(lock) {
boolean result = (state == SCHEDULED);
state = CANCELLED;
return result;
}
}
- scheduledExecutionTime()
作用
從此計時器的任務隊列中移除所有已取消(canceled)的任務。
返回值
從隊列中移除的任務數。
源碼
public long scheduledExecutionTime() {
synchronized(lock) {
return (period < 0 ? nextExecutionTime + period
: nextExecutionTime - period);
}
}
不能與fixed-delay執行式的重復任務搭配使用,也就是不用於schedule方法,應為schedule方法的(scheduled execution time)計划執行時間會偏移理想的計划時間,對她使用這個方法沒有無意義。
演示代碼清單:
/*
* CancelTest.java -- JDK 1.8
*/
package timer;
import java.util.Timer;
import java.util.TimerTask;
/**
* Description:
* <p>
* <p>
* @author ascribed
* @date 2019-04-03 Wed PM 19:43:04
*/
public class CancelTest {
public static void main(String[] args) {
Timer timer = new Timer();
MyTimerTask myTimerTask = new MyTimerTask("schedule");
TimerUtils.miscellaneous("Current time is : ", TimerUtils.current().getTime());
timer.schedule(myTimerTask, 3000, 2000); // 3.2.1
// timer.schedule(myTimerTask, 3000); // 3.3.2
// TimerUtils.schedtime(myTimerTask);
}
}
class MyTimerTask extends TimerTask {
private String name;
private Integer count = 0;
public MyTimerTask(String name) {
this.name = name;
}
@Override
public void run() {
if (count < 3) {
TimerUtils.miscellaneous("Current time is : ", TimerUtils.current().getTime());
System.out.println("Current exec name is : " + name);
count++;
} else {
cancel();
System.out.println("Task canceled");
System.exit(0);
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3.2.2 Timer的cancel(), purge()
- cancel()
作用
終止此計時器,丟棄
所有當前已安排的任務。
源碼
public void cancel() {
synchronized(queue) {
thread.newTasksMayBeScheduled = false;
queue.clear();
queue.notify(); // 防止隊列已為空的處理
}
}
- purge()
作用
purge,意為凈化;(將不需要的東西)從......清除,相比消滅顯得優雅一些。從此計時器的任務隊列中移除所有已取消
的任務。
返回值
從隊列中移除的任務數。
源碼
public int purge() {
int result = 0;
synchronized(queue) {
for (int i = queue.size(); i > 0; i--) {
if (queue.get(i).state == TimerTask.CANCELLED) {
queue.quickRemove(i);
result++;
}
}
if (result != 0)
queue.heapify();
}
return result;
}
}
演示代碼:
/*
* CancelTest.java -- JDK 1.8
*/
package timer;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
* Description:
* <p>
* <p>
* @author ascribed
* @date 2019-04-03 Wed PM 19:43:04
*/
public class CancelTest {
public static void main(String[] args) throws InterruptedException {
Timer timer = new Timer();
MyTimerTask task1 = new MyTimerTask("task1");
MyTimerTask task2 = new MyTimerTask("task2");
TimerUtils.miscellaneous("start time is : ", new Date());
// task1首次執行是距離現在時間2秒后,之后每隔2秒執行一次
// task2首次執行是距離現在時間1秒后,之后每隔2秒執行一次
timer.schedule(task1, 1000, 2000); // 奇數次執行
timer.schedule(task2, 2000, 2000); // 偶數次執行
// System.out.println("current canceled task number is : " + timer.purge());
Thread.sleep(5000); // 當前線程休眠5秒后cancel生效,沒有此句立即觸發cancel
TimerUtils.miscellaneous("cancel time is : ", new Date());
/*3.2.2.1
下面兩句執行完后程序只剩下后台線程,JRE判定當前程序結束
因為當前程序只有后台線程,所有前台線程結束,后台的工作無前台線程使用就是沒有意義的
*/
timer.cancel(); // 當前線程若檢測到timer對隊列中的任務進行調度則終止timer並從任務隊列移除所有任務
System.out.println("Tasks all canceled!"); // 若此句輸出后看到還有任務運行則停止所有運行的程序,這可能是之前運行的程序未終止
// 3.2.2.2
// task1.cancel(); // 當前線程每次檢測到timer對task1進行schedule取消task1
// System.out.println("current canceled task number is : " + timer.purge());
}
}
class MyTimerTask extends TimerTask {
private String name;
private Integer count = 0;
public MyTimerTask(String name) {
this.name = name;
}
@Override
public void run() {
if (count < 3) {
TimerUtils.miscellaneous("Current time is : ", TimerUtils.current()
.getTime());
System.out.println("Current exec name is : " + name);
count++;
} else {
cancel();
System.out.println("Task canceled");
System.exit(0);
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3.3 schedule()和scheduledAtFixedRate()的區別
兩種情況看區別一覽表
方法 | schedule | scheduleAtFixedRate |
---|---|---|
首次計划執行時間早於當前時間 | "fixed-delay",用代碼的形式理解就是scheduleAtfixedDelay();如果第一次執行時間被delay了,隨后的執行時間按照上一次實際執行完成的時間點 進行計算。 |
"fixed-rate",義如其名;如果第一次執行時間按照上一次開始的時間點 進行計算,並且為了趕上進度會多次執行任務,因此TimerTask中的執行體需要考慮同步 。 |
任務執行所需的時間超出任務的執行周期間隔 | 下一次執行時間相對於上一次實際執行完成的時間點 ,因此執行時間會不斷延后 。 |
下一次執行時間相對於上一次開始的時間點 ,因此執行時間一般不會延后 ,因此存在並發性 。 |
fixed-delay和fixed-rate執行的區別
- 對於fixed-delay執行講解:
如當前時間2020-01-01 00:01:00,period為2000毫秒,將開始執行時間提前6秒即2020-01-01 00:01:57秒,首次執行時間為2020-01-01 00:01:00而不是2020-01-01 00:01:57,代碼snippet:
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, -6); // L1
Timer timer = new Timer();
// 第一次執行為6秒前,之后么個兩秒鍾執行一次
timer.schedule(new TimerTask() {
@Override
public void run() { // L2
System.out.println("Scheduled exec time is : " + TimerUtils.sdf.format(scheduledExecutionTime()));
System.out.println("Task is being executed!");
}
}, calendar.getTime(), 2000);
如果任務時間為3000毫秒,第一次執行開始時間2020-01-01 00:01:00,第二次為2020-01-01 00:01:03而不是2020-01-01 00:01:02。
這里使用在任務線程休眠三秒來實現,注釋掉L1行代碼,在L1處添加代碼休眠三秒,代碼snippet:
Calendar calendar = Calendar.getInstance();
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Scheduled exec time is : " + TimerUtils.sdf.format(scheduledExecutionTime()));
System.out.println("Task is being executed!");
}
}, calendar.getTime(), 2000);
- 對於fixed-reate執行講解:
如當前時間2019-04-03 23:02:58,period為2000毫秒,將開始執行時間提前6秒即2019-04-03 23:02:52秒,首次執行時間為2019-04-03 23:02:52,控制台會看到開始會一下子執行如下4次任務:
Current time is : 2019-04-03 23:02:58
Scheduled exec time is : 2019-04-03 23:02:52
Task is being executed!
Scheduled exec time is : 2019-04-03 23:02:54
Task is being executed!
Scheduled exec time is : 2019-04-03 23:02:56
Task is being executed!
Scheduled exec time is : 2019-04-03 23:02:58
Task is being executed!
代碼snippet:
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, -6); // L1
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {// L3
System.out.println("Scheduled exec time is : " + TimerUtils.sdf.format(scheduledExecutionTime()));
System.out.println("Task is being executed!");
}
}, calendar.getTime(), 2000);
}
如果任務時間為3000毫秒,period為2000毫秒,當前時間2019-04-03 23:23:22,第一次執行開始時間2019-04-03 23:23:22,第二次執行時間2019-04-03 23:23:24,兩個任務執行時間段有交集。
注釋掉L1所在行,在L3處讓任務線程休眠三秒模仿執行時間為3秒的任務,代碼snippet:
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, -6); // L1
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
Thread.sleep(3000); // L3
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Scheduled exec time is : " + TimerUtils.sdf.format(scheduledExecutionTime()));
System.out.println("Task is being executed!");
}
}, calendar.getTime(), 2000);
注釋掉不需要的代碼觀察效果,演示代碼:
/*
* DifferenceTest.java -- JDK 1.8
*/
package timer;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
/**
* Description:
* <p>
* <p>
* @author ascribed
* @date 2019-04-03 Wed PM 21:47:38
*/
public class DifferenceTest {
public static void main(String[] args) {
Calendar calendar = TimerUtils.current();
TimerUtils.miscellaneous("Current time is : ", calendar.getTime());
// 設置成6秒前的時間,若當前時間為2016-12-28 00:00:06
// 那么設置之后時間變成2016-12-28 00:00:00
calendar.add(Calendar.SECOND, -6); // L1
Timer timer = new Timer();
// 第一次執行為6秒前,之后么個兩秒鍾執行一次
timer.schedule(new TimerTask() {
@Override
public void run() {
try {
Thread.sleep(3000); // L2
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Scheduled exec time is : " + TimerUtils.sdf.format(scheduledExecutionTime()));
System.out.println("Task is being executed!");
}
}, calendar.getTime(), 2000); // 此處有個語法糖,編譯器生成一個匿名類繼承抽象類TimerTask通過new實例化,這並不違反抽象類不能實例化這一原則
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
Thread.sleep(3000); // L3
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Scheduled exec time is : " + TimerUtils.sdf.format(scheduledExecutionTime()));
System.out.println("Task is being executed!");
}
}, calendar.getTime(), 2000);
}
}
4 實戰
實現兩個機器人
跳舞機器人:每隔兩秒打印最近一次計划的時間和執行內容
灌水機器人:模擬往桶里倒水,直到桶里的水滿為止
灌水機器人工作流程
灌水,如果水不滿,則一直工作;如果水滿,則停止工作。
跳舞機器人
跳舞,如果水不滿,則一直工作;如果水滿,則跳舞兩秒后停止工作。
代碼清單
DancingRobot.java、WaterRobot.java和Executor.java。
/*
* WaterRobot.java -- JDK 1.8
*/
package timer;
import java.util.Timer;
import java.util.TimerTask;
/**
* Description:
* <p>
* <p>
* @author ascribed
* @date 2019-04-02 Tue PM 16:44:17
*/
public class WaterRobot extends TimerTask {
private Timer timer;
// 最大容量5L
private Integer bucketCapacity = 0;
private final String unit = "L";
public WaterRobot(Timer timer) {
this.timer = timer;
}
@Override
public void run() {
// 灌水到桶滿為止
if (bucketCapacity < 5) {
System.out.println("Add 1L water into the bucket!");
bucketCapacity++;
} else {
// 水滿之后停止執行
System.out.println("The number of canceled task in timer is : " + timer.purge());
cancel();
System.out.println("The waterRot has been aborted");
System.out.println("The number of canceled task in timer is : " + timer.purge());
System.out.println("Current water is " + bucketCapacity + unit);
// 等待兩秒鍾,終止timer里面的所有內容
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
timer.cancel();
}
}
}
/*
* DancingRobot.java -- JDK 1.8
*/
package timer;
import java.text.SimpleDateFormat;
import java.util.TimerTask;
/**
* Description:
* <p>
* <p>
* @author ascribed
* @date 2019-04-02 Tue PM 16:35:12
*/
public class DancingRobot extends TimerTask {
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat(TimeConstants.DATE_FORMAT);
System.out.println("Scheduled exec time is : " + sdf.format(scheduledExecutionTime())); // 獲得最近一次任務執行的計划時間
System.out.println("Dancing happily!");
}
}
/*
* Executor.java -- JDK 1.8
*/
package timer;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
/**
* Description:
* <p>
* <p>
* @author ascribed
* @date 2019-04-02 Tue PM 16:54:04
*/
public class Executor {
public static void main(String[] args) {
Timer timer = new Timer();
Calendar calendar = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat(TimeConstants.DATE_FORMAT);
System.out.println("Current time is : " + sdf.format(calendar.getTime()));
DancingRobot dr = new DancingRobot();
WaterRobot wr = new WaterRobot(timer);
timer.schedule(dr, calendar.getTime(), 2000);
timer.scheduleAtFixedRate(wr, calendar.getTime(), 1000);
}
}
執行結果
Current time is : 2019-04-04 01:19:20
Scheduled exec time is : 2019-04-04 01:19:21
Dancing happily!
Add 1L water into the bucket!
Add 1L water into the bucket!
Add 1L water into the bucket!
Scheduled exec time is : 2019-04-04 01:19:23
Dancing happily!
Add 1L water into the bucket!
Add 1L water into the bucket!
Scheduled exec time is : 2019-04-04 01:19:25
Dancing happily!
The number of canceled task in timer is : 0
The waterRot has been aborted
The number of canceled task in timer is : 1
Current water is 5L
5 Timer的缺陷
天生的兩種缺陷
- 管理並發任務的缺陷
Timer有且僅有一個線程去執行定時任務,如果存在多個任務,且任務時間過長,會導致執行結果與預期不符。
演示代碼:
/*
* ExTimer.java -- JDK 1.8
*/
package timer;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
/**
* Description:
* <p>
* <p>
* @author ascribed
* @date 2019-04-03 Wed PM 23:37:41
*/
public class ConTimer {
public static void main(String[] args) {
Timer timer = new Timer();
ConTimerTask exTimerTask1 = new ConTimerTask("No.1", 2000);
ConTimerTask exTimerTask2 = new ConTimerTask("No.2", 2000);
Calendar calendar = TimerUtils.current();
TimerUtils.miscellaneous("Current time is : ", calendar
.getTime());
timer.schedule(exTimerTask1, calendar.getTime());
timer.schedule(exTimerTask2, calendar.getTime());
// timer.scheduleAtFixedRate(exTimerTask1, calendar.getTime(), 2000);
// timer.scheduleAtFixedRate(exTimerTask2, calendar.getTime(), 2000);
}
}
class ConTimerTask extends TimerTask {
private String name;
private long costTime;
public ConTimerTask(String name, long costTime) {
this.setName(name);
this.costTime = costTime;
}
@Override
public void run() {
System.out.println(name + "'s current exec time is : " + TimerUtils.sdf.format(Calendar.getInstance()
.getTime())); // 輸出當前時間
try {
Thread.sleep(costTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "'s finish time is : " + TimerUtils.sdf.format(Calendar.getInstance()
.getTime())); // 輸出costTime之后的時間
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
執行結果:
Current time is : 2019-04-04 01:04:24
No.1's current exec time is : 2019-04-04 01:04:24
No.1's finish time is : 2019-04-04 01:04:26
No.2's current exec time is : 2019-04-04 01:04:26
No.2's finish time is : 2019-04-04 01:04:28
- 當任務拋出異常時的缺陷
如果TimerTask
拋出RuntimeException,Timer會停止所有任務的運行。
演示代碼:
/*
* ExTimer.java -- JDK 1.8
*/
package timer;
import java.util.Timer;
import java.util.TimerTask;
/**
* Description:
* <p>
* <p>
* @author ascribed
* @date 2019-04-04 Thu AM 00:33:14
*/
public class ExTimer {
public static void main(String[] args) {
Timer timer = new Timer();
ExTimerTask task1 = new ExTimerTask("task1");
ExTimerTask task2 = new ExTimerTask("task2");
timer.scheduleAtFixedRate(task1, 1000, 1000);
timer.scheduleAtFixedRate(task2, 1000, 2000 );
}
}
class ExTimerTask extends TimerTask {
private String name;
public ExTimerTask(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name);
throw new IllegalStateException();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
執行結果:
task1
Exception in thread "Timer-0" java.lang.IllegalStateException
at timer.ExTimerTask.run(ExTimer.java:37)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
- TimerTask是一次性
定時器(Timer)的TimerTask實例只能schedule一次,再次調用會拋出運行時異常IllegalStateException,這是一個運行時異常。
解決方法有二:一是反射修改state字段,二是每次用new一個TimerTask。
Timer的使用禁區
- 對時效性要求較高的多任務並發作業
- 對復雜的任務的調度
JDK里的Timer
由於本人水平有限,故有關Timer內部細節可參考這篇文章。