
定时器是一种实际开发中非常常用的组件。
比如网络通信中,如果对方500ms内没有返回数据,则断开连接尝试重连。
比如一⼀个Map,希望里面的某个key在3s之后过期(自动删除)。
类似于这样的场景就需要用到定时器。

schedule包含两个参数,第一个参数指定即将要执行的任务代码,第⼆个参数指定多长时间之后执行(单位为毫秒)
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello");
}
}, 3000);一个带优先级队列(不要使用PriorityBlockingQueue,容易死锁!)
队列中的每个元素是一个Task对象
Task中带有一个时间属性,队首元素就是即将要执行的任务
同时有一个worker线程一直扫描队首元素,看队首元素是否需要执行
1)Timer类提供的核心接口为schedule,用于注册一个任务,并指定这个任务多长时间后执行
public class MyTimer {
public void schedule(Runnable command, long after) {
// TODO
}
}2)Task类用于描述⼀个任务(作为Timer的内部类),里面包含一个Runnable对象和一个time(毫秒时间戳)
这个对象需要放到优先队列中,因此需要实现 Comparable 接口
class MyTask implements Comparable<MyTask> {
public Runnable runnable;
// 为了⽅便后续判定, 使⽤绝对的时间戳.
public long time;
public MyTask(Runnable runnable, long delay) {
this.runnable = runnable;
// 取当前时刻的时间戳 + delay, 作为该任务实际执⾏的时间戳
this.time = System.currentTimeMillis() + delay;
}
@Override
public int compareTo(MyTask o) {
// 这样的写法意味着每次取出的是时间最⼩的元素.
// 到底是谁减谁?? 俺也记不住!!! 随便写⼀个, 执⾏下, 看看效果~~
return (int)(this.time - o.time);
}
}3)Timer实例中,通过PriorityQueue来组织若干个Task对象
通过schedule来往队列中插入一个个Task对象
class MyTimer {
// 核⼼结构
private PriorityQueue<MyTask> queue = new PriorityQueue<>();
// 创建⼀个锁对象
private Object locker = new Object();
public void schedule(Runnable command, long after) {
// 根据参数, 构造 MyTask, 插⼊队列即可.
synchronized (locker) {
MyTask myTask = new MyTask(runnable, delay);
queue.offer(myTask);
locker.notify();
}
}
}4)Timer类中存在一个worker线程,一直不停的扫描队首元素,看看是否能执行这个任务
所谓"能执行"指的是该任务设定的时间已经到达了
// 在这⾥构造线程, 负责执⾏具体任务了.
public MyTimer() {
Thread t = new Thread(() -> {
while (true) {
try {
synchronized (locker) {
// 阻塞队列, 只有阻塞的⼊队列和阻塞的出队列, 没有阻塞的查看队⾸元素.
while (queue.isEmpty()) {
locker.wait();
}
MyTask myTask = queue.peek();
long curTime = System.currentTimeMillis();
if (curTime >= myTask.time) {
// 时间到了, 可以执⾏任务了
queue.poll();
myTask.runnable.run();
} else {
// 时间还没到
locker.wait(myTask.time - curTime);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}因为 Timer 的调度是异步的(由独立线程执行),主线程在调用 timer.schedule 后会 “继续往下执行”,不会停在那里等 2 秒。
可以类比成:你(主线程)点了一份外卖(定时任务),然后继续做自己的事(打印 hello main);外卖小哥(Timer 线程)会在 2 秒后把外卖送到(执行 hello timer)。
timer.cancel())来释放资源。创建一个类表示定时任务,需包含任务逻辑和执行时间(用于判断何时执行)。
ArrayList 管理多个任务,每次需遍历找到 “执行时间最早” 的任务,效率较低。PriorityQueue),基于 “执行时间” 排序,让时间最早的任务始终在队首,可高效获取待执行任务。schedule 方法实现该方法负责将任务添加到优先级队列中,完成任务的注册。
额外创建一个独立线程,循环执行以下逻辑:
schedule 方法向队列中添加任务,另一个是定时器内部的线程从队列中获取并执行任务。synchronized 关键字、ReentrantLock 等锁机制来保证线程安全。计算任务的目标执行时间:任务的目标执行时间 = 任务提交时的系统时间 + 设定的延时时间(毫秒)
在 Lambda 表达式中,若涉及锁(如 synchronized 或 Lock),this 的指向始终是外部类的实例,而非 Lambda 表达式本身(因为 Lambda 表达式不是匿名内部类,它没有自己的 this)。
this(指向匿名内部类实例),因此在匿名内部类中使用 this 时,指向的是该内部类自身。this,其内部的 this 与所在外部类的 this 完全一致(即指向外部类的当前实例)。注意:在锁里的this 指向的是外面的类的
this。在 Lambda 表达式内部使用 this 时,this 指向的是创建 Lambda 表达式的外部类的当前实例(即包围 Lambda 表达式的那个类的实例)。
Lambda 表达式本质是 “函数式接口的实例化简化”,但它不是一个独立的类(不同于匿名内部类),也不会生成独立的类对象。它更像是一个 “嵌入在外部类中的代码块”,因此不具备自身的 this 引用。
this 等价于外部类的 this,指向外部类实例。this 引用(因它不是独立的类实例)。实现单线程的定时器,如果一口气来10000个任务一个一个的任务执行也太慢了,可以结合定时器来使用

扫描线程持续监控任务队列,当检测到任务执行时间到达时,将该任务从定时队列中取出,提交到执行线程池的任务队列中;执行线程池中的多个线程并行从队列中获取任务并执行,从而实现任务的高效并发处理。
针对 “一口气注册 100000 个 14:00 执行的任务” 这类大规模定时任务场景,该架构能有效解决单线程执行的性能瓶颈:
package thread;
// 这样的写法基于抽象类的方式定义 MyTimerTask
// 这样的定义虽然确实可以, 写起来有点麻烦.
// 还有另外的写法.
//abstract class MyTimerTask implements Runnable {
// @Override
// public abstract void run();
//}
import java.util.PriorityQueue;
import java.util.concurrent.Executors;
class MyTimerTask implements Comparable<MyTimerTask> {
private Runnable task;
// 记录任务要执行的时刻
private long time;
public MyTimerTask(Runnable task, long time) {
this.task = task;
this.time = time;
}
@Override
public int compareTo(MyTimerTask o) {
return (int) (this.time - o.time);
// return (int) (o.time - this.time);
}
public long getTime() {
return time;
}
public void run() {
task.run();
}
}
// 自己实现一个定时器
class MyTimer {
private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
// 直接使用 this 作为锁对象, 当然也是 ok 的
private Object locker = new Object();
public void schedule(Runnable task, long delay) {
synchronized (locker) {
// 以入队列这个时刻作为时间基准.
MyTimerTask timerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);
queue.offer(timerTask);
locker.notify();
}
}
public MyTimer() {
// 创建一个线程, 负责执行队列中的任务
Thread t = new Thread(() -> {
try {
while (true) {
synchronized (locker) {
// 取出队首元素
// 还是加上 while
while (queue.isEmpty()) {
// 这里的 sleep 时间不好设定!!
locker.wait();
}
MyTimerTask task = queue.peek();
if (System.currentTimeMillis() < task.getTime()) {
// 当前任务时间, 如果比系统时间大, 说明任务执行的时机未到
locker.wait(task.getTime() - System.currentTimeMillis());
} else {
// 时间到了, 执行任务
task.run();
queue.poll();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
}
}
public class demo38 {
public static void main(String[] args) {
MyTimer timer = new MyTimer();
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 3000");
}
}, 3000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 2000");
}
}, 2000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 1000");
}
}, 1000);
Executors.newScheduledThreadPool(4);
}
}PriorityQueue 实现简易定时器(MyTimer)的例子,相当于自己实现了一个简化版的 java.util.Timer。PriorityQueue 实现简易定时器(MyTimer)的例子,相当于自己实现了一个简化版的 java.util.Timer。Timer timer = new Timer();
timer.schedule(task, delay);但不用 Java 内置的 Timer 或 ScheduledExecutorService,而是自己实现时间调度机制。
MyTimerTask —— 封装任务与执行时间class MyTimerTask implements Comparable<MyTimerTask> {
private Runnable task; // 要执行的任务
private long time; // 任务应该执行的绝对时间(单位:毫秒)
public MyTimerTask(Runnable task, long time) {
this.task = task;
this.time = time;
}
public long getTime() { return time; }
public void run() { task.run(); }
@Override
public int compareTo(MyTimerTask o) {
return (int)(this.time - o.time); // 时间早的任务排在前面
}
}作用:
Runnable)和执行时间绑定起来;
Comparable 接口,让 PriorityQueue 能自动按照时间排序。
2.MyTimer —— 定时器调度核心class MyTimer {
private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
private Object locker = new Object();
public MyTimer() {
Thread t = new Thread(() -> {
try {
while (true) {
synchronized (locker) {
while (queue.isEmpty()) {
locker.wait();
}
MyTimerTask task = queue.peek();
long current = System.currentTimeMillis();
if (current < task.getTime()) {
// 时间还没到,等待差值时间
locker.wait(task.getTime() - current);
} else {
// 时间到了,执行并出队
queue.poll().run();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
}
public void schedule(Runnable task, long delay) {
synchronized (locker) {
MyTimerTask timerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);
queue.offer(timerTask);
locker.notify(); // 唤醒线程重新判断是否有更早的任务
}
}
}逻辑解释:
while (true))。
wait() 等待。
wait(remainingTime)。
run() 并出队。
💡 使用 PriorityQueue 的好处:
public class demo38 {
public static void main(String[] args) {
MyTimer timer = new MyTimer();
timer.schedule(() -> System.out.println("hello 3000"), 3000);
timer.schedule(() -> System.out.println("hello 2000"), 2000);
timer.schedule(() -> System.out.println("hello 1000"), 1000);
}
}

目标:实现一个简易定时器,支持 schedule(Runnable task, long delay),按时间顺序执行任务。
核心约束/设计选择(隐含假设):
PriorityQueue 存放待执行任务,按执行时间排序。
wait()/notify() 协调(最基础的同步手段)。
Runnable.run()(默认在调度线程里直接执行)。
MyTimerTask 的设计(封装任务与执行时间)class MyTimerTask implements Comparable<MyTimerTask> {
private Runnable task;
private long time; // 绝对执行时间(毫秒)
public MyTimerTask(Runnable task, long time) { ... }
public long getTime() { return time; }
public void run() { task.run(); }
@Override public int compareTo(MyTimerTask o) { return (int)(this.time - o.time); }
}Runnable 本身没有时间属性,包装成对象能把“任务”和“什么时候执行”绑定在一起,便于排序和管理。
PriorityQueue 通过元素比较来决定队首元素。实现 compareTo 让队列始终能把最早执行的任务放在队首,便于只关注队首任务即可决定下一步动作。
run() 方法:对外统一调用 run(),便于未来替换实现或在调度线程外执行(若改成线程池执行只需改 run() 的调用位置)。
compareTo 内直接做 (int)(this.time - o.time) 会溢出,应使用 Long.compare(this.time, o.time)。
原因:两个 long 相减再转 int 可能丢失或溢出,导致错排。
time 存绝对时间(System.currentTimeMillis() + delay)还是相对延迟(delay)有差别:
PriorityQueue 存任务 —— 为什么是好选择PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
peek() 即可知道下一次要执行的任务,而无需遍历所有任务。
PriorityQueue 不是线程安全 的 —— 必须外部加锁(本例用 synchronized(locker))。
PriorityQueue 对重复时间、相等时间等行为是可接受的,但若需要稳定性(相同时间的先后顺序),可能需要额外字段(序号)保证一致性。
locker 和 synchronizedprivate Object locker = new Object();
synchronized (locker) { ... }
locker.wait(...);
locker.notify();PriorityQueue 不是线程安全的;schedule() 由任意线程调用(外部线程),调度线程在后台访问队列。必须用同步保护队列的并发访问,避免数据结构破坏(race condition、heap 结构损坏)。
wait() / notify() 必须在同步块内调用(Java 语言要求),因此选择用 locker 作为监视器对象。
synchronized(this)?可以用 this,但用独立的 locker 更明确、可替换(例如以后把锁替换为 ReentrantLock 时更方便),也可避免暴露锁给外部代码(若 this 被外部 synchronized 使用,容易增加耦合)。
schedule() 要 notify()?timer.schedule(...) 插入任务后调用 locker.notify();
wait(remaining) —— 等待当前队首任务的到期时间。如果新插入的任务比当前队首任务更早(也就是需要更早执行),必须唤醒调度线程,让它重新计算应该等待多久(或者立刻执行新任务)。否则将错过更早任务的执行时刻。
notify() 的作用是唤醒等待线程,调度线程就会从 wait() 返回并重新检查队列和时间差。
notify() 还是 notifyAll()?notify() 足够且更高效。
notifyAll() 或更复杂的协调机制。
notifyAll() 更稳妥(避免死锁或遗漏唤醒),但代价是多线程被无谓唤醒。
while (true) {
synchronized (locker) {
while (queue.isEmpty()) {
locker.wait();
}
MyTimerTask task = queue.peek();
long current = System.currentTimeMillis();
if (current < task.getTime()) {
locker.wait(task.getTime() - current);
} else {
queue.poll();
task.run();
}
}
}逐行解释并说明为什么这样写:
while (true):线程一直运行,负责不断调度任务。设计选择:常驻调度线程,直到被中断/关闭。
shutdown 标志)来优雅终止线程。
synchronized (locker):在整个检查-等待-取出的过程中必须持锁,保证检查队列状态和 wait/poll 的原子性,防止竞态。
while (queue.isEmpty()) locker.wait();
while 而非 if:防止虚假唤醒(Java 的 wait() 可被 spuriously wake up),以及在被唤醒后需要再次检查队列是否真有任务。
if,虚假唤醒会导致线程继续执行,peek() 可能返回 null,抛 NPE。
MyTimerTask task = queue.peek(); — 只读取队首但不出队。
poll()?因为如果时间未到不能丢弃或提前执行;peek() 允许检查时间再决定是否等待或执行。
if (current < task.getTime()) locker.wait(task.getTime() - current);
wait(timeout) 让线程在剩余时间内等待,减少 CPU 消耗(比 busy-wait 好)。当到期或被 notify()(或被中断/虚假唤醒)返回后,循环会重新检查条件。
long 差值要小心负数(若系统时间变动或计算误差),要确保传给 wait 的值非负(可 Math.max(0, ...))。
else { queue.poll(); task.run(); }
poll() 再 run() 可避免任务在执行中再次被.peek到(线程安全)。
task.run() 抛异常会中断调度线程的循环(若异常没有捕获)。因此在实际中应把 task.run() 包在 try-catch 内,防止一个任务崩掉整个调度线程。
wait(timeout) 而不是 sleep(timeout)?sleep 只会睡眠当前线程,不与锁/条件交互:如果使用 sleep,在等待期间无法被 notify() 提前唤醒(只能被 interrupt() 打断)。那样当插入更早的任务时就无法及时调整等待时长,会导致延迟执行或复杂的轮询机制。wait(timeout) 则在锁释放的同时进入等待状态,可接受 notify() 提前唤醒并重新判断。
System.currentTimeMillis() vs System.nanoTime()代码中用 System.currentTimeMillis() + delay 计算绝对执行时刻。
currentTimeMillis()?有风险吗?currentTimeMillis() 返回墙钟时间(wall-clock),会受系统时间调整(NTP 校正、人工修改、夏令时等)影响。若系统时钟向后调整,可能产生长时间等待或错过任务;向前调整则可能导致任务提前执行。
System.nanoTime()(单调递增,用于测量间隔,不受系统时钟调整影响)。计算方式:记录 deadlineNano = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(delay),比较 nanoTime() 与 deadlineNano。
nanoTime 更安全。
currentTimeMillis()。
task.run(),无需线程池或任务转发。
ExecutorService(线程池)执行:调度线程只负责时间判断并 executor.submit(task)。这样调度线程不会被单个任务阻塞;但需要处理任务提交失败或线程池饱和的情况。
MyTimerTask t = queue.poll();
executor.execute(() -> {
try { t.run(); } catch (Throwable e) { ... }
});package thread;
import java.util.PriorityQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
class MyTimerTask implements Comparable<MyTimerTask> {
private Runnable task;
private long time;
private volatile boolean cancelled = false;
public MyTimerTask(Runnable task, long time) {
this.task = task;
this.time = time;
}
@Override
public int compareTo(MyTimerTask o) {
return Long.compare(this.time, o.time);
}
public long getTime() {
return time;
}
public void run() {
if (!cancelled) {
task.run();
}
}
public void cancel() {
cancelled = true;
}
public boolean isCancelled() {
return cancelled;
}
}
// 模拟实现的定时器 - 使用线程池但保持原有逻辑
class MyTimer {
private final PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
private final Object locker = new Object();
private final ExecutorService workerPool;
private final Thread scannerThread;
private volatile boolean running = true;
// 自定义线程工厂,给线程命名
private static class TimerThreadFactory implements ThreadFactory {
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
TimerThreadFactory(String poolName) {
namePrefix = poolName + "-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
t.setDaemon(true);
return t;
}
}
public MyTimer() {
this(1); // 默认使用1个工作线程
}
public MyTimer(int workerThreads) {
// 创建工作线程池
workerPool = Executors.newFixedThreadPool(workerThreads, new TimerThreadFactory("timer-worker"));
// 创建扫描线程(保持原有设计)
scannerThread = new Thread(() -> {
try {
scanAndExecute();
} catch (InterruptedException e) {
System.out.println("扫描线程被中断");
}
}, "timer-scanner");
scannerThread.setDaemon(true);
scannerThread.start();
}
public void schedule(Runnable task, long delay) {
if (delay < 0) {
throw new IllegalArgumentException("延迟时间不能为负数");
}
MyTimerTask timerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);
synchronized (locker) {
queue.offer(timerTask);
locker.notify(); // 唤醒扫描线程
System.out.println(Thread.currentThread().getName() + " - 添加任务,延迟: " + delay + "ms, 队列大小: " + queue.size());
}
}
// 取消所有任务
public void cancel() {
synchronized (locker) {
for (MyTimerTask task : queue) {
task.cancel();
}
queue.clear();
locker.notify();
}
}
// 停止定时器
public void stop() {
running = false;
workerPool.shutdownNow();
scannerThread.interrupt();
synchronized (locker) {
locker.notifyAll();
}
}
// 获取待执行任务数量
public int getTaskCount() {
synchronized (locker) {
return queue.size();
}
}
// 核心扫描和执行逻辑
private void scanAndExecute() throws InterruptedException {
while (running && !Thread.currentThread().isInterrupted()) {
synchronized (locker) {
// 等待队列不为空
while (queue.isEmpty() && running) {
locker.wait();
}
if (!running) {
break;
}
// 检查队首任务
MyTimerTask task = queue.peek();
if (task == null) {
continue;
}
long currentTime = System.currentTimeMillis();
long taskTime = task.getTime();
long waitTime = taskTime - currentTime;
if (waitTime <= 0) {
// 任务时间已到,从队列移除
queue.poll();
if (!task.isCancelled()) {
// 使用线程池执行任务,而不是直接在当前线程执行
System.out.println(Thread.currentThread().getName() + " - 提交任务到线程池执行");
workerPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " - 开始执行任务");
task.run();
System.out.println(Thread.currentThread().getName() + " - 任务执行完成");
});
}
} else {
// 等待到任务执行时间
System.out.println(Thread.currentThread().getName() + " - 等待 " + waitTime + "ms");
locker.wait(waitTime);
}
}
}
System.out.println("扫描线程退出");
}
}
// 测试类
public class demo38 {
public static void main(String[] args) throws InterruptedException {
System.out.println("=== 模拟实现定时器(线程池版)===");
// 创建定时器,使用2个工作线程
MyTimer timer = new MyTimer(2);
// 添加多个任务
for (int i = 1; i <= 5; i++) {
final int taskId = i;
timer.schedule(() -> {
System.out.println(Thread.currentThread().getName() + " - 任务" + taskId + " 执行");
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, i * 1000L); // 1秒, 2秒, 3秒...
}
// 添加一个长时间任务
timer.schedule(() -> {
System.out.println(Thread.currentThread().getName() + " - 长时间任务开始");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " - 长时间任务结束");
}, 6000);
// 监控队列大小
Thread monitorThread = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
System.out.println("监控 - 待执行任务数: " + timer.getTaskCount());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "monitor-thread");
monitorThread.start();
// 等待所有任务执行
Thread.sleep(15000);
System.out.println("\n=== 测试任务取消 ===");
// 测试取消功能
MyTimer timer2 = new MyTimer(1);
// 添加一些任务
for (int i = 1; i <= 3; i++) {
final int taskId = i;
timer2.schedule(() -> {
System.out.println("定时器2 - 任务" + taskId + " 执行");
}, i * 2000L);
}
Thread.sleep(2500);
// 取消剩余任务
System.out.println("取消前任务数: " + timer2.getTaskCount());
timer2.cancel();
System.out.println("取消后任务数: " + timer2.getTaskCount());
Thread.sleep(2000);
// 停止定时器
timer.stop();
timer2.stop();
System.out.println("测试完成");
}
}
时间轮适合时间精度高的定时器,优先级队列实现的定时器执行任务多的
时间轮通过 “循环数组 + 链表” 按时间单位分组管理任务,适合时间粒度固定(如 10ms/100ms)、任务数量庞大的场景。
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
/**
* 时间轮定时器
* 特点:按固定时间粒度(tickMs)分组任务,高效处理大量定时任务,时间精度由tickMs决定
*/
public class TimeWheelTimer {
// 时间轮的时间粒度(每个格子代表的毫秒数,决定精度)
private final long tickMs;
// 时间轮的格子数量(循环数组大小)
private final int wheelSize;
// 时间轮总覆盖时间范围(tickMs * wheelSize)
private final long totalRangeMs;
// 循环数组:每个格子对应一个时间单位,存储该单位内的任务链表
private final List<TimerTask>[] buckets;
// 当前指针位置(指向当前时间单位对应的格子)
private int currentIndex;
// 当前时间轮的绝对时间(毫秒)
private long currentTime;
// 工作线程:推动时间轮前进并执行任务
private final Thread workerThread;
// 锁:保证线程安全
private final ReentrantLock lock = new ReentrantLock();
// 标记是否运行
private volatile boolean isRunning;
// 定时任务封装
static class TimerTask {
Runnable task; // 实际任务
long delayMs; // 延迟时间(用户传入)
long targetTime; // 目标执行时间(绝对时间)
public TimerTask(Runnable task, long delayMs, long targetTime) {
this.task = task;
this.delayMs = delayMs;
this.targetTime = targetTime;
}
}
/**
* 初始化时间轮
* @param tickMs 时间粒度(毫秒,如100ms,精度越高性能消耗略大)
* @param wheelSize 格子数量(总范围 = tickMs * wheelSize)
*/
public TimeWheelTimer(long tickMs, int wheelSize) {
this.tickMs = tickMs;
this.wheelSize = wheelSize;
this.totalRangeMs = tickMs * wheelSize;
// 初始化循环数组(每个格子是一个任务链表)
this.buckets = new List[wheelSize];
for (int i = 0; i < wheelSize; i++) {
buckets[i] = new LinkedList<>();
}
this.currentTime = System.currentTimeMillis();
this.currentIndex = 0;
this.isRunning = true;
// 启动工作线程
this.workerThread = new Thread(this::run, "TimeWheel-Worker");
this.workerThread.start();
}
// 添加定时任务
public void schedule(Runnable task, long delayMs) {
if (delayMs < 0) {
throw new IllegalArgumentException("延迟时间不能为负数");
}
long targetTime = System.currentTimeMillis() + delayMs;
TimerTask timerTask = new TimerTask(task, delayMs, targetTime);
lock.lock();
try {
// 计算任务应放入的格子索引
int bucketIndex = calculateBucketIndex(targetTime);
buckets[bucketIndex].add(timerTask);
} finally {
lock.unlock();
}
}
// 计算任务所在的格子索引
private int calculateBucketIndex(long targetTime) {
// 计算与当前时间的差值
long offset = targetTime - currentTime;
// 若超过总范围,取模(简化处理,多层时间轮可优化)
if (offset >= totalRangeMs) {
offset %= totalRangeMs;
}
// 计算索引(当前指针 + 偏移的格子数)% 总格子数
return (currentIndex + (int) (offset / tickMs)) % wheelSize;
}
// 工作线程逻辑:推动时间轮
private void run() {
while (isRunning) {
try {
// 休眠一个时间粒度,推动时间轮前进
Thread.sleep(tickMs);
lock.lock();
try {
currentTime += tickMs; // 更新当前时间
currentIndex = (currentIndex + 1) % wheelSize; // 移动指针
// 取出当前格子的所有任务
List<TimerTask> currentBucket = buckets[currentIndex];
if (!currentBucket.isEmpty()) {
// 执行所有任务(用新线程执行,避免阻塞时间轮)
for (TimerTask task : currentBucket) {
new Thread(task.task, "Task-Executor").start();
}
currentBucket.clear(); // 清空当前格子
}
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
if (!isRunning) {
break;
}
}
}
}
// 停止定时器
public void stop() {
isRunning = false;
workerThread.interrupt();
}
// 测试
public static void main(String[] args) throws InterruptedException {
// 创建时间轮:100ms精度,100个格子(总覆盖10秒)
TimeWheelTimer timer = new TimeWheelTimer(100, 100);
// 添加1000个任务(模拟大量任务)
for (int i = 0; i < 1000; i++) {
final int num = i;
// 任务延迟1-3秒随机分布
long delay = 1000 + new Random().nextInt(2000);
timer.schedule(() -> System.out.println("任务" + num + "执行,延迟" + delay + "ms"), delay);
}
// 运行5秒后停止
Thread.sleep(5000);
timer.stop();
}
}基于优先级队列(小顶堆)按任务执行时间排序,每次取出最早到期的任务执行,实现简单但大量任务时性能略低。
import java.util.PriorityQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 基于堆(优先级队列)的定时器
* 特点:实现简单,适合任务数量不多的场景,时间精度高(依赖系统时间)
*/
public class HeapBasedTimer {
// 优先级队列(小顶堆):按任务执行时间升序排序
private final PriorityQueue<TimerTask> taskQueue;
// 锁和条件变量:保证线程安全和等待唤醒
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
// 工作线程:执行到期任务
private final Thread workerThread;
// 标记是否运行
private volatile boolean isRunning;
// 定时任务封装(实现Comparable按执行时间排序)
static class TimerTask implements Comparable<TimerTask> {
Runnable task; // 实际任务
long targetTime; // 目标执行时间(绝对时间)
public TimerTask(Runnable task, long targetTime) {
this.task = task;
this.targetTime = targetTime;
}
@Override
public int compareTo(TimerTask o) {
// 按目标时间升序排序(小顶堆)
return Long.compare(this.targetTime, o.targetTime);
}
}
public HeapBasedTimer() {
this.taskQueue = new PriorityQueue<>();
this.isRunning = true;
// 启动工作线程
this.workerThread = new Thread(this::run, "HeapTimer-Worker");
this.workerThread.start();
}
// 添加定时任务
public void schedule(Runnable task, long delayMs) {
if (delayMs < 0) {
throw new IllegalArgumentException("延迟时间不能为负数");
}
long targetTime = System.currentTimeMillis() + delayMs;
TimerTask timerTask = new TimerTask(task, targetTime);
lock.lock();
try {
taskQueue.add(timerTask);
condition.signal(); // 唤醒等待的工作线程
} finally {
lock.unlock();
}
}
// 工作线程逻辑:循环执行最早到期的任务
private void run() {
while (isRunning) {
lock.lock();
try {
// 循环检查队列是否为空
while (taskQueue.isEmpty()) {
condition.await(); // 空队列时等待
}
// 取出最早到期的任务
TimerTask task = taskQueue.peek();
long currentTime = System.currentTimeMillis();
if (currentTime < task.targetTime) {
// 未到期,等待剩余时间
condition.await(task.targetTime - currentTime);
} else {
// 到期,执行任务并移除
taskQueue.poll();
new Thread(task.task, "Task-Executor").start();
}
} catch (InterruptedException e) {
if (!isRunning) {
break;
}
} finally {
lock.unlock();
}
}
}
// 停止定时器
public void stop() {
isRunning = false;
workerThread.interrupt();
}
// 测试
public static void main(String[] args) throws InterruptedException {
HeapBasedTimer timer = new HeapBasedTimer();
// 添加少量任务(适合该定时器的场景)
timer.schedule(() -> System.out.println("任务1执行(延迟1000ms)"), 1000);
timer.schedule(() -> System.out.println("任务2执行(延迟2000ms)"), 2000);
timer.schedule(() -> System.out.println("任务3执行(延迟500ms)"), 500);
// 运行3秒后停止
Thread.sleep(3000);
timer.stop();
}
}