首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 多線程定時任務實戰:Timer 與 TimerTask 深度解析

     編輯:Java 相關知識

前言

java.util.Timer 是 Java 早期提供的定時調度工具類,配合 TimerTask 可實現單次或周期性任務執行。雖然後來有 ScheduledExecutorService 等更強大的替代品,但理解 Timer 內部機制對掌握定時調度原理仍有裨益。

基礎用法示例

示例 1:簡單周期任務

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 執行一次
    }
}

示例 2:交替執行雙任務

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);
        }
    }
}

Timer 核心源碼深度解析

核心數據結構

private final TaskQueue queue = new TaskQueue();      // 優先級隊列(最小堆)
private final TimerThread thread = new TimerThread(queue); // 調度線程
  • TaskQueue:基於數組的最小堆,按 nextExecutionTime 排序,確保最近要執行的任務在隊首。
  • TimerThread:繼承 Thread,運行 mainLoop() 死循環,不斷從隊列取任務執行。

構造方法啟動調度線程

public Timer() {
    this("Timer-" + serialNumber());
}

public Timer(String name) {
    thread.setName(name);
    thread.start(); // 關鍵:new Timer() 即啟動一個後台線程
}

mainLoop() 核心調度邏輯


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) { }
    }
}

schedule() 任務入隊流程


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(); // 喚醒調度線程
    }
}

TaskQueue 堆操作精華

  • fixUp(int k):上浮調整,新任務插入後維持最小堆性質。
  • fixDown(int k):下沉調整,移除隊首或更新時間後維持堆序。
  • rescheduleMin(long newTime):更新隊首任務執行時間並重新排序。

Timer 的已知缺陷與替代方案

問題說明替代方案
單線程串行執行任務耗時長會阻塞後續任務ScheduledExecutorService 線程池
異常導致線程終止任務拋出未捕獲異常會殺死 TimerThreadScheduledExecutorService 可捕獲異常不影響其他任務
不支持動態調度無法根據業務結果動態調整下次執行時間Quartz、Spring Task、xxl-job
系統時間敏感系統時間修改會影響執行精度使用 ScheduledExecutorService 或分布式調度器

總結

Timer 基於「單線程 + 最小堆優先隊列」實現定時調度,原理清晰但功能受限。現代項目建議使用 ScheduledExecutorService 或專業調度框架(Quartz、xxl-job、Spring Task)。理解 Timer 源碼有助於掌握「延遲隊列、時間輪、堆排序」等核心調度思想。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved