之前大部分人在遇到需要使用定時任務的時候首先會想到Timer類,用法一般就是:
new Timer("timer").schedule(new TimerTask() {
@Override
public void run() {
System.out.println("執行任務");
}
}, 1000L, 1000L);
不過在JDK5.0之後就不建議使用這個Timer了,因為它有很多的缺陷。
在新的java.util.concurrent包中的ScheduledExecutorService可以替代這個Timer:
使用方法舉例:
ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
exec.scheduleAtFixedRate(new Runnable() {
public void run() {
try{
throw new RuntimeException();
}catch (Exception e){
System.out.println("RuntimeException catched");
}
}
}, 1000, 5000, TimeUnit.MILLISECONDS);
理由:
第1:Timer在執行所有定時任務的時候只會創建一個線程。如果某個任務執行時間過長,那麼將破壞其他TimeTask的定時精確性。
第2:如果TimerTask拋出一個UncheckedException,那麼Timer將表現出很挫的行為。Timer並不捕獲異常,因此當TimerTask拋出UncheckedException時候將終止定時線程,比如常見的一個NullPointerException。這種情況下,Timer不會恢復線程執行,而是會錯誤的任務整個Timer都取消掉。因此,已經被調度但尚未執行的Task將不會執行了,新的任務也不會調度,也就是常見的線程洩露。
而ScheduledThreadPoolExecutor能正確處理這些表現出錯誤行為的任務。
因此,如果您的JDK是5.0以上的,那麼建議不要使用過時的Timer類了。
最後,如果要用到高級功能,比如在某個星期的周五,在國慶節當天的幾點幾分,在每天凌晨幾點幾分執行之類的定時任務,就需要用到大名鼎鼎的Quartz框架了,一般用法:
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
JobDetail job = newJob(SimpleJob.class)
.withIdentity("job1", "group1")
.build();
CronTrigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(cronSchedule("0/20 * * * * ?"))
.build();
Date ft = sched.scheduleJob(job, trigger);
sched.start();
據我所知這個就是定時任務的最佳實踐了,不知道大家還有沒有更好的建議。