Java定时任务调度:用ExecutorService取代Timer

本文详细介绍了在Java并发编程中,使用ExecutorService相较于Java Timer的优势,特别是如何防止未检查异常导致的定时任务失败问题。通过示例代码展示在ExecutorService中的正确实现方式,确保定时任务的正常执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

《Java并发编程实战》一书提到的用ExecutorService取代Java Timer有几个理由,我认为其中最重要的理由是:
如果TimerTask抛出未检查的异常,Timer将会产生无法预料的行为。Timer线程并不捕获异常,所以 TimerTask抛出的未检查的异常会终止timer线程。这种情况下,Timer也不会再重新恢复线程的执行了;它错误的认为整个Timer都被取消了。此时,已经被安排但尚未执行的TimerTask永远不会再执行了,新的任务也不能被调度了。

stackoverflow上也有关于此问题的讨论:
[url]http://stackoverflow.com/questions/409932/java-timer-vs-executorservice[/url]

Timer的问题:

package com.ljn.timer;

import java.util.Date;
import java.util.Timer;

/**
* @author lijinnan
* @date:2013-11-25 下午3:27:43
*/
public class TimerException {

public static void main(String[] args) {
System.out.println("start:" + new Date());
Timer timer = new Timer();
int delay = 1000;
int period = 2000;
timer.schedule(new OKTask(), delay * 2, period); //"OKTask" does not get chance to execute
timer.schedule(new ErrorTask(), delay, period); //exception in "ErrorTask" will terminate the Timer
}

/*输出:
start:Mon Nov 25 17:49:53 CST 2013
ErrorTask is executing...
error:Mon Nov 25 17:49:55 CST 2013
Exception in thread "Timer-0" java.lang.RuntimeException: something wrong
at com.ljn.timer.ErrorTask.run(ErrorTask.java:14)
*/

}



用ExecutorService则正常:

package com.ljn.timer;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
* @author lijinnan
* @date:2013-11-25 下午3:35:39
*/
public class ScheduledExecutorServiceTest {

public static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);

public static void main(String[] args){
System.out.println("start:" + new Date());
ErrorTask errorTask = new ErrorTask();
OKTask okTask = new OKTask();
int delay = 1000;
int period = 2000;
scheduledExecutorService.scheduleAtFixedRate(errorTask, delay, period, TimeUnit.MILLISECONDS); //"ErrorTask" throws Exception and then stopes.
scheduledExecutorService.scheduleAtFixedRate(okTask, delay * 2, period, TimeUnit.MILLISECONDS); //"OKTask" is executed periodically, not affected by "ErrorTask"

//scheduledExecutorService.shutdown();
}

/*
start:Mon Nov 25 17:54:22 CST 2013
ErrorTask is executing...
error occurs:Mon Nov 25 17:54:24 CST 2013
OKTask is executed:Mon Nov 25 17:54:24 CST 2013
OKTask is executed:Mon Nov 25 17:54:26 CST 2013
OKTask is executed:Mon Nov 25 17:54:28 CST 2013
......
*/

}



另外开发中常常会让任务在每天的指定时间点运行,示例如下:

package com.ljn.timer;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
* @author lijinnan
* @date:2013-11-25 下午5:18:55
*/
public class FixedDatetimeTaskTest {

public static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

public static void main(String[] args) throws Exception {
System.out.println("start:" + new Date());

//每天的02:30:00执行任务
long delay = Helper.calcDelay(2, 30, 0);
long period = Helper.ONE_DAY;
scheduledExecutorService.scheduleAtFixedRate(new OKTask(), delay, period, TimeUnit.MILLISECONDS);
}

}



文章中用到的其他类:

package com.ljn.timer;

import java.util.Date;
import java.util.TimerTask;

public class ErrorTask extends TimerTask {

@Override
public void run() {
try {
System.out.println("ErrorTask is executing...");
Thread.sleep(1000);
System.out.println("error occurs:" + new Date());
throw new RuntimeException("something wrong");
} catch (InterruptedException e) {
}
}

}



package com.ljn.timer;

import java.util.Date;
import java.util.TimerTask;


public class OKTask extends TimerTask {

@Override
public void run() {
System.out.println("OKTask is executed:" + new Date());
}
}



package com.ljn.timer;

import org.joda.time.DateTime;

/**
* @author lijinnan
* @date:2013-11-25 下午5:17:40
*/
public class Helper {

private Helper() {}

public static final long ONE_DAY = 60 * 60 * 24;

public static long calcDelay(int hour, int minute, int second) {
if (!(0 <= hour && hour <=23 && 0 <= minute && minute <=59 && 0 <=second && second <= 59)) {
throw new IllegalArgumentException();
}
return calcDelay(fixed(hour, minute, second));
}

private static long calcDelay(DateTime targetDatetimeOfToday) {
long delay = 0;
DateTime now = new DateTime();

//时间点已过,只好延时到明天的这个时间点再执行
if (now.isAfter(targetDatetimeOfToday)) {
delay = now.plusDays(1).getMillis() - now.getMillis();

//时间点未到
} else {
delay = targetDatetimeOfToday.getMillis() - now.getMillis();
}

return delay;
}

/**
* 返回这样一个DateTime对象:
* 1.日期为今天
* 2.时分秒为参数指定的值
* @param hour 0-23
* @param minute 0-59
* @param second 0-59
* @return
*/
private static DateTime fixed(int hour, int minute, int second) {

return new DateTime()
.withHourOfDay(hour).withMinuteOfHour(minute).withSecondOfMinute(second);
}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值