0%

Timer的使用

Java Timer 定时任务调度详解

Timer 是 Java 原生提供的定时任务调度工具,虽然功能相对简单,但在简单场景下可以满足定时执行任务的需求。本文将详细解析 Timer 的核心组件、调度方式及使用细节。

Timer 核心组件

Timer 框架主要由两个核心类构成:TimerTimerTask

TimerTask:任务载体

TimerTask 是一个抽象类,实现了 Runnable 接口,代表一个可以被 Timer 调度的任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class TimerTask implements Runnable {
// 任务状态常量
static final int VIRGIN = 0; // 未调度
static final int SCHEDULED = 1; // 已调度(等待执行)
static final int EXECUTED = 2; // 已执行(非重复任务)
static final int CANCELLED = 3; // 已取消

int state = VIRGIN; // 初始状态为未调度

// 抽象方法,需用户实现具体任务逻辑
public abstract void run();

// 取消任务
public boolean cancel() { ... }

// 获取任务下次执行时间
public long scheduledExecutionTime() { ... }
}

注意:虽然 TimerTask 实现了 Runnable,但它并非独立线程,其 run 方法由 Timer 内部的线程调用(单线程执行)。

Timer:调度器

Timer 负责管理和调度 TimerTask,内部通过一个线程(TimerThread)执行所有任务,并使用优先级队列(TaskQueue)存储待执行任务。

核心功能:

  • 调度任务(一次性或周期性执行)。
  • 管理任务队列(添加、移除、排序)。
  • 控制任务执行线程的生命周期。

任务调度方式

Timer 提供了两种主要的调度方法:schedule(固定时延)和 scheduleAtFixedRate(固定速率),适用于不同场景。

1. schedule:固定时延调度

特点:任务的下次执行时间 = 上一次任务实际完成时间 + 间隔时间(不受任务执行耗时影响,会顺延)。

方法定义

1
2
3
4
5
6
7
8
9
10
11
// 延迟 delay 毫秒后执行一次
public void schedule(TimerTask task, long delay);

// 在指定时间执行一次
public void schedule(TimerTask task, Date time);

// 延迟 delay 毫秒后,每隔 period 毫秒重复执行
public void schedule(TimerTask task, long delay, long period);

// 在指定时间开始,每隔 period 毫秒重复执行
public void schedule(TimerTask task, Date firstTime, long period);

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
Timer timer = new Timer();
// 延迟 1 秒后,每 2 秒执行一次
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("任务执行:" + new Date());
try {
Thread.sleep(1000); // 模拟任务耗时 1 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 1000, 2000);

执行结果(时间间隔为上次完成 + 2 秒):

1
2
3
任务执行:10:00:01  // 首次执行(延迟 1 秒)
任务执行:10:00:04 // 10:00:01 + 1 秒(执行耗时) + 2 秒(间隔)= 10:00:04
任务执行:10:00:07 // 以此类推

2. scheduleAtFixedRate:固定速率调度

特点:任务的下次执行时间 = 上一次任务计划执行时间 + 间隔时间(尽可能保证固定频率,即使任务执行耗时超过间隔)。

方法定义

1
2
3
4
5
// 延迟 delay 毫秒后,以 period 为周期固定速率执行
public void scheduleAtFixedRate(TimerTask task, long delay, long period);

// 在指定时间开始,以 period 为周期固定速率执行
public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period);

示例:(复用上面的任务代码,仅修改调度方法)

1
timer.scheduleAtFixedRate(new TimerTask() { ... }, 1000, 2000);

执行结果(时间间隔为上次计划时间 + 2 秒):

1
2
3
任务执行:10:00:01  // 首次执行(计划时间)
任务执行:10:00:03 // 计划时间 10:00:01 + 2 秒 = 10:00:03(即使任务耗时 1 秒,仍按计划时间执行)
任务执行:10:00:05 // 以此类推

注意:如果任务执行耗时超过周期,会导致任务 “追赶执行”(上一个未完成,下一个已触发,会在当前任务结束后立即执行)。

任务管理方法

1. cancel ():取消 Timer 所有任务

1
2
3
4
5
6
7
public void cancel() {
synchronized(queue) {
thread.newTasksMayBeScheduled = false; // 标记不再接受新任务
queue.clear(); // 清空任务队列
queue.notify(); // 唤醒等待的线程,使其退出
}
}

作用

  • 终止 Timer 线程,不再执行任何任务(包括已调度但未执行的任务)。
  • 已执行的任务不受影响。

2. purge ():清理已取消的任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int purge() {
int result = 0;
synchronized(queue) {
// 遍历队列,移除状态为 CANCELLED 的任务
for (int i = queue.size(); i > 0; i--) {
if (queue.get(i).state == TimerTask.CANCELLED) {
queue.quickRemove(i);
result++;
}
}
if (result != 0) queue.heapify(); // 重新堆化队列
}
return result; // 返回清理的任务数量
}

作用:释放已取消任务占用的资源,优化队列性能。

Timer 的局限性

  1. 单线程执行:所有任务由 Timer 内部的单个线程执行,若某个任务耗时过长,会阻塞后续任务(即使到了执行时间)。
  2. 异常终止风险:若任务执行时抛出未捕获异常,Timer 线程会直接终止,导致所有后续任务失效。
  3. 时间精度低:依赖系统时间,且调度精度受线程调度影响(毫秒级,但实际可能有偏差)。
  4. 不支持复杂场景:如任务依赖、动态调整周期、分布式调度等,需依赖 Quartz、ScheduledExecutorService 等工具。

使用建议

  • 简单场景:可使用 Timer(如定时清理临时文件、简单的周期性任务)。
  • 复杂场景:推荐使用 ScheduledExecutorService(JUC 提供,支持多线程、异常处理更完善)或第三方框架(如 Quartz)。
  • 避免耗时任务:若必须使用 Timer,确保任务执行时间远小于调度周期,避免阻塞。

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10