Java Timer原理与自定义实现解析

键盘旁的猫
键盘旁的猫
发布时间:2026-10-15| 3人浏览过
原创

在Java开发中,Timer常用于执行定时任务。本文将拆解Timer的工作原理,并指导读者如何通过PriorityBlockingQueue和Wait/Notify机制,从零构建一个具备优先级调度、低CPU占用且线程安全的自定义定时器,同时规避并发冲突陷阱。

标准库中的Timer机制

定时器(Timer)的本质是代替人工在指定时间点执行特定任务。Java标准库提供的java.util.Timer类提供了基础的定时功能。其核心方法schedule(TimerTask task, long delay)允许用户指定延迟时间(毫秒)后执行一个TimerTask。

public static void main(String[] args) {
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("定时器任务! ");
        }
    }, 1000);
}

上述代码创建了一个定时器实例,并提交了一个任务,使其在当前时间1秒后执行。虽然标准库使用方便,但理解其底层逻辑有助于在处理复杂调度需求时进行定制化开发。

自定义定时器的核心设计要素

要实现一个可靠的自定义定时器,需要解决三个核心问题:任务存储结构、执行线程调度以及时间判定的准确性。

第一步:设计任务对象与存储结构

提交的任务由“ Runnable执行体 ”和“ 实际执行时间 ”两部分组成。为了能够高效地获取“最早到期”的任务,需要使用支持优先级的数据结构。PriorityBlockingQueue是一个线程安全的无界阻塞队列,非常适合用于维护按时间排序的任务集合。

首先定义MyTask类来封装任务和触发时间:

class MyTask implements Comparable<MyTask> {
    private final Runnable runnable;
    private final long triggerTime; // 绝对执行时间戳

    public MyTask(Runnable runnable, long triggerTime) {
        this.runnable = runnable;
        this.triggerTime = triggerTime;
    }

    public long getTriggerTime() {
        return triggerTime;
    }

    public void run() {
        runnable.run();
    }

    /**
     * 根据触发时间排序,确保队头总是最早执行的任务
     */
    @Override
    public int compareTo(MyTask o) {
        if (this.triggerTime < o.triggerTime) return -1;
        else if (this.triggerTime == o.triggerTime) return 0;
        else return 1;
    }
}

接着构建定时器骨架,包含扫描线程和任务队列:

public class MyTimer {
    private Thread scanThread;
    private final PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    private final Object locker = new Object();

    public void schedule(Runnable runnable, long delay) {
        // 计算绝对触发时间
        MyTask task = new MyTask(runnable, System.currentTimeMillis() + delay);
        queue.put(task);
        synchronized (locker) {
            locker.notify();
        }
    }

    // ... 其他逻辑
}

第二步:优化扫描线程的CPU消耗

简单的轮询方式会导致极高的CPU占用。例如,如果当前时间为8:00,而下一个任务在12:00执行,忙循环会浪费大量资源。正确的做法是采用“阻塞式”等待:wait()直到接近任务执行时间或收到新任务通知。

修改后的扫描线程逻辑如下:


private void initScanThread() {
    scanThread = new Thread(() -> {
        while (true) {
            try {
                synchronized (locker) {
                    // 1. 取出队头最早的任务
                    MyTask task = queue.take();
                    long now = System.currentTimeMillis();

                    // 2. 判断是否到达执行时间
                    if (now < task.getTriggerTime()) {
                        // 未到时间,放回队列,并阻塞等待剩余时间
                        queue.put(task); // 重新入堆以维持优先级顺序
                        long waitTime = task.getTriggerTime() - now;
                        if (waitTime > 0) {
                            locker.wait(waitTime);
                        } else {
                            // 等待期间已过,应继续执行
                             queue.put(task);
                        }
                    } else {
                        // 已到时间,释放锁执行任务
                        task.run();
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    });
    scanThread.start();
}

这里引入了locker.wait(time)。当任务未到期时,线程进入阻塞状态,系统不会分配CPU周期,直到超时或被notify唤醒。这种方式大幅降低了空闲时的CPU消耗。

第三步:解决并发竞态条件

在上述逻辑中,queue.take()与后续的wait()必须处于同一个锁保护范围内,否则会出现竞态条件。假设线程A在执行完take后被挂起,此时线程B插入了一个更早执行的任务并调用了notify。由于线程A尚未调用wait,这次唤醒将失效。当线程A恢复执行并调用wait(longTime)时,它将错误地等待一个极长的时间段,导致新插入的紧急任务被延误。

为确保原子性,synchronized (locker)块必须包裹从取任务到判断并调用的整个流程。同时,每次通过schedule插入新任务后调用locker.notify(),可以强制唤醒等待线程,使其重新评估队列中的最早任务是否需要提前执行。

总结

通过自定义定时器,我们不仅掌握了Java Timer的基本用法,更深入理解了并发编程中的阻塞等待与线程间通信。使用PriorityBlockingQueue管理任务顺序,配合wait/notify机制实现精准的时间调度,是处理此类问题的经典模式。以上就是Java定时器Timer原理与自定义实现的详细内容,更多关于Java多线程与定时任务的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。