延迟任务和周期任务
在 Java 中,管理延迟和周期任务主要有两种方式:
Timer 和 ScheduledThreadPoolExecutor
Timer
Timer 可以管理延迟任务和周z期任务,但存在一些缺陷:
- Timer 是单线程的,如果某个任务执行时间过长可能会破坏其它 TimerTask 的定时准确性。
- Timer 线程不会捕捉异常,若 TimerTask 抛出未受查异常则将终止整个 Timer 线程,该状况称为线程泄露(Thread Leakage)。
- Timer 只支持绝对时间而非相对时间的调度机制,因此对系统时钟变化很敏感。
ScheduledThreadPoolExecutor:
- 支持多线程来执行延时任务或周期任务。
- 只支持基于相对时间的调度机制,对系统时钟不敏感。
- 能处理表现出错误行为的任务。
当 TimerTask 抛出运行时异常
由于 Timer 线程不会捕捉异常,当 TimerTask 抛出未受查异常时会终止整个 Timer 线程,且无法恢复,整个 Timer 因此而结束。这导致了已经被调度但尚未执行的 TimerTask 不会再执行,而新的任务也不能再被调度。
下面的例程中,TimerTask 将主动抛出一个 RuntimeException,程序并没有在运行六秒后退出,而是只运行了 1 秒就结束了,且抛出了异常:java.lang.IllegalStateException: Timer already cancelled。
import java.util.Timer;
import java.util.TimerTask;
import static java.util.concurrent.TimeUnit.SECONDS;
public class OutOfTime {
public static void main(String[] args) throws InterruptedException {
Timer timer = new Timer();
timer.schedule(new ThrowTask(), 1);
SECONDS.sleep(1);
timer.schedule(new ThrowTask(), 1);
SECONDS.sleep(5);
}
static class ThrowTask extends TimerTask {
public void run() {
throw new RuntimeException();
}
}
}