java.util.Timer 是 Java 早期提供的定時調度工具類,配合 TimerTask 可實現單次或周期性任務執行。雖然後來有 ScheduledExecutorService 等更強大的替代品,但理解 Timer 內部機制對掌握定時調度原理仍有裨益。
import java.util.Timer;
import java.util.TimerTask;
public class TraditionalTimerTest01 {
public static void main(String[] args) {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("這是一個定時器任務!");
}
}, 1000, 2000); // 延遲 1s 首次執行,之後每 2s 執行一次
}
}
public class TraditionalTimerTest02 {
public static void main(String[] args) {
new Timer().schedule(new MyTimerTask01(), 4000);
}
static class MyTimerTask01 extends TimerTask {
@Override
public void run() {
System.out.println("我是 TimerTask1,我被執行了!");
new Timer().schedule(new MyTimerTask02(), 2000);
}
}
static class MyTimerTask02 extends TimerTask {
@Override
public void run() {
System.out.println("我是 TimerTask2,我被執行了!");
new Timer().schedule(new MyTimerTask01(), 4000);
}
}
}
private final TaskQueue queue = new TaskQueue(); // 優先級隊列(最小堆)
private final TimerThread thread = new TimerThread(queue); // 調度線程
nextExecutionTime 排序,確保最近要執行的任務在隊首。Thread,運行 mainLoop() 死循環,不斷從隊列取任務執行。public Timer() {
this("Timer-" + serialNumber());
}
public Timer(String name) {
thread.setName(name);
thread.start(); // 關鍵:new Timer() 即啟動一個後台線程
}
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized (queue) {
// 隊列為空且未取消,線程等待
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty()) break; // 隊列空且已取消,結束循環
task = queue.getMin(); // 獲取最優先任務
synchronized (task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue;
}
long currentTime = System.currentTimeMillis();
long executionTime = task.nextExecutionTime;
if (taskFired = (executionTime <= currentTime)) {
if (task.period == 0) { // 單次任務
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // 周期任務
queue.rescheduleMin(
task.period < 0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // 未到執行時間,精確等待
queue.wait(executionTime - currentTime);
}
if (taskFired) task.run(); // 執行任務(在同步塊外,避免長時間持有鎖)
} catch (InterruptedException e) { }
}
}
public void schedule(TimerTask task, long delay, long period) {
if (delay < 0) throw new IllegalArgumentException("Negative delay.");
if (period <= 0) throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis() + delay, -period); // 負 period 表示 fixed-delay
}
private void sched(TimerTask task, long time, long period) {
// ... 參數校驗 ...
synchronized (queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized (task.lock) {
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException("Task already scheduled or cancelled");
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
}
queue.add(task); // 加入優先隊列
if (queue.getMin() == task) queue.notify(); // 喚醒調度線程
}
}
| 問題 | 說明 | 替代方案 |
|---|---|---|
| 單線程串行執行 | 任務耗時長會阻塞後續任務 | ScheduledExecutorService 線程池 |
| 異常導致線程終止 | 任務拋出未捕獲異常會殺死 TimerThread | ScheduledExecutorService 可捕獲異常不影響其他任務 |
| 不支持動態調度 | 無法根據業務結果動態調整下次執行時間 | Quartz、Spring Task、xxl-job |
| 系統時間敏感 | 系統時間修改會影響執行精度 | 使用 ScheduledExecutorService 或分布式調度器 |
Timer 基於「單線程 + 最小堆優先隊列」實現定時調度,原理清晰但功能受限。現代項目建議使用 ScheduledExecutorService 或專業調度框架(Quartz、xxl-job、Spring Task)。理解 Timer 源碼有助於掌握「延遲隊列、時間輪、堆排序」等核心調度思想。