1.背景: 最近新出了一个需求,"根据用户输入的时间,进行任务的调度执行"
2.由于之前只是使用一些调度的框架,没有时间研究实现原理,今天就想着看看,定时的底层原理实现过程,方便日后的业务开发
3.首先,不打算直接看源码进行说明.直接说下定时中使用的几个原理点Timer进行说明
①定时任务中只有一个线程和一个任务队列(该队列是一个优先级队列,基于最小堆实现,时间距现在最近的任务排在开头)
②任务的执行是根据当前时间进行判断是否执行,如果任务的执行时间大于当前时间就在队列上等待,否则更新任务的下次时间给任务,然后进行任务的执行
③只要线程启动后,就会在一个while循环中不断的执行任务
4.下面附上源码(源码很简单的),请大家根据上面的解析,进行查看就好理解啦
注意:下面只是Timer中的run方法的代码
public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
}
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; // No action required, poll queue again
}
//获取当前时间和下次任务执行时间
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
//判断任务执行时间是否小于当前时间,表示小于,就说明可以执行了
if (taskFired = (executionTime<=currentTime)) {
//如果任务的执行周期是0,说明只要执行一次就好了,就从队列中移除它,这样下一次就不会获取到该任务了
if (task.period == 0) {
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else {
//重新设置该任务下一次的执行时间
//如果之前设置的period小于0,就用当前时间-period,等于就是当前时间加上周期值
//这里的下次执行时间就是当前的执行时间加上周期值
//这里涉及到是否以固定频率调用任务的问题,下面再详细讲解
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
//如果任务的执行时间还没到,就计算出还有多久才到达执行时间,然后线程进入休眠
if (!taskFired)
queue.wait(executionTime - currentTime);
}
//如果任务的执行时间到了,就执行这个任务
if (taskFired)
task.run();
} catch(InterruptedException e) {
}
}
}
5.看完Timer的大致执行过程后,再简单说下ScheduledThreadPoolExecutor这个类
这个类中和Timer不同点是使用了线程池技术,多个线程来执行队列中的任务,其他的原理和Timer是类似的,希望日后看一些技术可以举一反三!!!