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);
}
注意,我们要添加一个Thread.sleep(delay * 2),允许Timer的线程在Junit测试停止执行之前运行任务。
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);
}
这个就不写Thread.sleep了,等不起。
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指令 - 为了运行这个简单的例子,我们需要实际停止线程。
@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);
}
在实际的自定义线程实现中,一般会支持停止线程。
这里为了简单使用Thread类的stop方法,这个方法已经被废弃了。
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内部抛出的运行时异常会杀死该线程,因此整个任务将无法再运行;
- 使用ScheduledThreadExecutor, 当前任务被取消停止,其余任务还将继续运行
6 结论
本教程说明了使用Java内置的简单而灵活的Timer和TimerTask基础结构的许多方法,可以快速实现调度任务。
如果你需要更复杂和完整的解决方案,在Java的世界里还有Quartz等开源框架。