任务调度之Java Timer
1、前言
在Java中如果需要定时执行某些任务,可以使用java.util包提供Timer和TimerTask,在后台线程中调度任务。 简单来说,TimerTask是要执行的任务,Timer是调度程序。
2、运行一个定时任务
使用Timer的来运行一个定时任务
@Test public void testTimer_SchedulingTaskOnce() throws Exception { TimerTask task = new TimerTask() { public void run() { System.out.println("Task performed on: " + new Date() + "n" + "Thread's name: " + Thread.currentThread().getName()); } }; Timer timer = new Timer("Timer"); long delay = 1000L; timer.schedule(task, delay); Thread.sleep(delay * 2); }
3. 重复执行定时任务
如何定义一个以预定义的时间间隔运行的任务。使用scheduleAtFixedRate(repeatedTask,delay,period)方法。它在指定的延迟(delay)之后,间隔指定的时间段(period),重复执行的任务。
@Test public void testTimer_SchedulingRepeatedTask() throws InterruptedException { TimerTask repeatedTask = new TimerTask() { public void run() { System.out.println("Task performed on " + new Date()); } }; Timer timer = new Timer("Timer"); long delay = 1000L; long period = 1000L; timer.scheduleAtFixedRate(repeatedTask, delay, period); Thread.sleep(delay * 10); }
如果执行因为一些原因(例如垃圾收集或其他后台活动)而延迟,则会快速连续执行两次或更多次执行以“赶上”正常的执行次数。
3.1 定义每天执行的任务
下面,定义一个每天都执行的任务:
@Test public void testTimer_SchedulingDailyTask() { TimerTask repeatedTask = new TimerTask() { public void run() { System.out.println("Task performed on " + new Date()); } }; Timer timer = new Timer("Timer"); long delay = 1000L; long period = 1000L * 60L * 60L * 24L; timer.scheduleAtFixedRate(repeatedTask, delay, period); }
4.取消定时器和定时任务
可以通过以下几种方式取消任务的执行:
4.1 取消运行中的TimerTask
TimerTask本身的实现类里,通过在run()方法的中调用TimerTask.cancel()方法:
@Test
public void testTimer_CancelingTimerTask() throws InterruptedException {
TimerTask task = new TimerTask() {
public void run() {
System.out.println("Task performed on " + new Date());
cancel();
}
};
Timer timer = new Timer("Timer");
timer.scheduleAtFixedRate(task, 1000L, 1000L);
Thread.sleep(1000L * 10);
}
执行上面的代码,会发现TimerTask只被执行了一次。
4.2 取消定时器
通过在Timer对象上调用Timer.cancel()方法:
@Test public void testTimer_CancelingTimer() throws InterruptedException { TimerTask task = new TimerTask() { public void run() { System.out.println("Task performed on " + new Date()); } }; Timer timer = new Timer("Timer"); timer.scheduleAtFixedRate(task, 1000L, 1000L); Thread.sleep(1000L * 1); timer.cancel(); Thread.sleep(1000L * 9); }
执行上面的代码,会发现TimerTask也只被执行了一次。
4.3 在运行中停止TimerTask的Thread
还可以在任务的run方法中停止线程,从而取消整个任务: 注意运行实现中的TODO指令 - 为了运行这个简单的例子,我们需要实际停止线程。Thread类的stop方法,这个方法已经被废弃了。
@Test public void testTimer_StoppingThread()throws InterruptedException { TimerTask task = new TimerTask() { public void run() { System.out.println("Task performed on " + new Date()); // TODO: 假如在这里停止 Thread.currentThread().stop(); } }; Timer timer = new Timer("Timer"); timer.scheduleAtFixedRate(task, 1000L, 1000L); Thread.sleep(1000L * 2); }
在实际的自定义线程实现中,一般会支持停止线程。
5.Timer VS ExecutorService
可以利用ExecutorService来执行定时器任务,而不是使用定时器。以下是如何以指定间隔运行重复任务的示例:
@Test public void testTimer_SchedulingRepeatedTask_ByScheduledExecutorService() throws InterruptedException { TimerTask repeatedTask = new TimerTask() { public void run() { System.out.println("Task performed on " + new Date()); } }; ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); long delay = 1000L; long period = 1000L; executor.scheduleAtFixedRate(repeatedTask, delay, period, TimeUnit.MILLISECONDS); Thread.sleep(delay + period * 3); executor.shutdown(); }
那么Timer和ExecutorService之间的主要区别是什么:
- 定时器可以对系统时钟的变化敏感; ScheduledThreadPoolExecutor不是
- Timer只有一个执行线程; ScheduledThreadPoolExecutor可以配置任意数量的线程
- 在TimerTask中抛出的运行时异常会杀死一个线程,从而导致Timer死机:(即计划任务将不再运行。ScheduledThreadExecutor不仅捕获运行时异常,还允许在需要时处理它们(通过重写afterExecute方法ThreadPoolExecutor)。抛出异常的任务将被取消,但其他任务将继续运行。
在 JDK1.5 之后,基本不使用 Timer 进行任务调度了。
6 总结
使用Java内置的简单而灵活的Timer和TimerTask基础结构的许多方法,可以快速实现调度任务。如果需要更复杂和完整的解决方案,在Java的世界里还有Quartz等开源框架。