Java中Timer实现定时调度的原理
✔️ 引言
Timer
实现定时调度的基本原理:
- 创建 Timer 对象:当你创建一个
Timer
对象时,它会实例化一个线程(不是守护线程),这个线程用于执行计划任务。 - 添加任务:你可以使用
schedule
或scheduleAtFixedRate
方法向Timer
添加任务。这些方法接受一个TimerTask
对象和一个延迟时间(以及可选的重复间隔)。 - 内部存储:
Timer
内部使用一个优先级队列(具体是TaskQueue
类)来存储要执行的任务。队列中的每个元素都是一个表示要执行的任务的TimerTask
对象。队列根据任务的执行时间进行排序,以确保任务按照预定的时间顺序执行。 - 执行线程:
Timer
类的线程在后台运行,并定期检查任务队列。当线程发现队列中有任务到达其预定的执行时间时,它会从队列中取出该任务并执行它。 - 处理重复任务:对于需要重复执行的任务,
Timer
会重新计算下一个执行时间,并将任务重新放入队列中。这样,当任务的下一次执行时间到达时,线程会再次从队列中取出并执行它。 - 取消任务:你可以使用
Timer.cancel()
方法来取消所有已调度的任务,或者使用TimerTask.cancel()
方法来取消单个任务。取消的任务将从队列中移除,并且不会再次调度。 - 注意事项:虽然
Timer
在简单场景下可以很好地工作,但它并不是最适合所有场景的定时任务解决方案。特别是,在需要更复杂的调度需求或在并发环境中,使用ScheduledExecutorService
可能是更好的选择。
总而言之,Java中的Timer
类通过内部使用一个优先级队列来存储和管理定时任务,并通过一个单独的线程来检查和执行这些任务,从而实现了定时调度功能。
✔️JDK 中Timer类的定义
Java中的Timer
类是一个定时调度器,用于在指定的时间点执行任务。JDK 中Timer类的定义如下:
public class Timer {
/**
*
* The timer task queue.This data structure is shared with the timer
* thread. The timer produces tasks, via its various schedule calls,
* and the timer thread consumes, executing timer tasks as appropriate,
* and removing them from the queue when they're obsolete.
*/
private final TaskOueue queue = new TaskOueue() ;
/**
* The timer thread
*/
private final TimerThread thread = new TimerThread(queue);
}
以上就是 Timer
中最重要的两入成员变量:
任务的定时调度的核心代码就在TimerThread 中:
/**
* @author xinbaobaba
*/
class TimerThread extends Thread {
//标志位
boolean newTasksMayBeScheduled = true;
/**
* 存储 TimerTask 的队列
*/
private TaskQueue queue ;
TimerThread(TaskQueue queue) {
this .queue = queue;
}
public void run() {
try {
mainLoop();
} finally {
synchronized (queue) {
newTasksMayBeScheduled = false;
queue.clear();
}
}
}
/**
* 主要的计时器循环。
*/
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized (queue) {
//等待队列变为非空
while (queue.isEmpty() && newTasksMayBeScheduled)
queue .wait() ;
if (queue.isEmpty())
// 队列为空,将永远保持为空; 线程终止
break;
//队列非空;查看第一个事件并执行相应操作
long currentTime, executionTime;
task = queue .getMin();
synchronized (task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue .removeMin() ;
//无需执行任何操作,再次轮询队列
continue;
}
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
if (taskFired = (executionTime <= currentTime)) {
if (task.period == 0) {// 非重复,移除
queue.removeMin);
task.state = TimerTask.EXECUTED;
} else {// 重复任务,重新安排
queue.rescheduleMin(
task.period < ? currentTime - task.period : executionTime + task.period);
}
}
}
if (!taskFired) // 任务尚未触发;等待
queue .wait(executionTime - currentTime);
}
if (taskFired) // 任务触发;运行它,不持有锁
task.run();
} catch (InterruptedException e) {
}
}
}
}
可以看到,TimerThread
的实际是在运行mainLoop方法,这个方法一进来就是一个while(true)的循环,他在循环中不断地从TaskQueue
中取出第一个任务,然后判断他是否到达执行时间了,如果到了,就触发任务执行。否则就继续等一会再次执行。
不断地重复这个动作,从队列中取出第一个任务进行判断,执行。。。
这样只要有新的任务加入队列,就在队列中按照时间排队,然后唤醒timerThread
重新检查队列进行执行就可以了。代码如下:
private void sched(TimerTask task, long time, long period) {
if (time < 0) {
throw new IllegalArgumentException("Illegal execution time.");
}
// Constrain value of period sufficiently to prevent numeric
// overflow while still being effectively infinitely large.
if (Math.abs(period) > (Long.MAX VALUE >> 1))
period >>= 1;
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();
}
}
✔️拓展知识仓
✔️优缺点
Timer 类用于实现定时任务,最大的好处就是他的实现非常简单,特别的轻量级,因为它是Java内置的,所以只需要简单调用就行了。
但是他并不是特别好的解决定时任务的方案,因为他存在以下问题: