概述
文章目录
- 使用例子
- 常用API
- 工作原理
- 总结
使用例子
package com.myself.study;
import java.util.Timer;
import java.util.TimerTask;
public class TimerDemo {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("timer执行业务逻辑...");
}
}, 1000, 1000);
Timer timer1 = new Timer();
timer1.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println("timer1执行业务逻辑...");
}
}, 1000, 1000);
}
}
常用API
// delay延时之后执行task,只执行一次
public void schedule(TimerTask task, long delay);
// 当时间到达time指定的时间时执行task,只执行一次
public void schedule(TimerTask task, Date time);
// delay延时之后执行一次task,然后每隔period时间执行一次task
public void schedule(TimerTask task, long delay, long period);
// 当时间到达firstTime指定的时间时执行一次task,然后每隔period时间执行一次task
public void schedule(TimerTask task, Date firstTime, long period);
// delay延时之后执行一次task,然后每隔period时间执行一次task
public void scheduleAtFixedRate(TimerTask task, long delay, long period);
// 当时间到达firstTime指定的时间时执行一次task,然后每隔period时间执行一次task
public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period);
Timer常用的API就是上述6个以schedule开头的方法,大体分为单次调度和周期性调度两类。其中对于周期性调度的方法需要特别说明的是,schedule和scheduleAtFixedRate正常情况下功能无差,但是在特殊情况下就会有部分差异,那就是当正在被调度的任务执行耗时很长,超过了period指定的时间间隔,schedule调度会在(任务结束时间 + period时间间隔)点再调度下次任务,而scheduleAtFixedRate调度则会尽量按照period对应的原始时间点调度下次任务。
比如,有一个任务原定周期执行间隔10s,0s处开始执行一次任务,因某些原因耗时13s,那么schedule和scheduleAtFixedRate运行时间点分别如下:
schedule:0s 13s 23s 33s …
scheduleAtFixedRate: 0s 13s 20s 30s …
工作原理
Timer对象内部持有一个TimerThread工作线程以及一个TaskQueue工作队列,调度任务首先通过Timer的schdule*相关方法提交到TaskQueue任务队列中,然后TimerThread工作线程业务逻辑中,通过while(true)死循环不断从TaskQueue队列中每次取出一个任务调用其run方法执行,所以说Timer定时器调度任务是单线程的,如果正在被调度的任务耗时太长就会阻塞TaskQueue队列中其他任务的执行。
class TimerThread extends Thread {
/**
* This flag is set to false by the reaper to inform us that there
* are no more live references to our Timer object. Once this flag
* is true and there are no more tasks in our queue, there is no
* work left for us to do, so we terminate gracefully. Note that
* this field is protected by queue's monitor!
*/
boolean newTasksMayBeScheduled = true;
/**
* Our Timer's queue. We store this reference in preference to
* a reference to the Timer so the reference graph remains acyclic.
* Otherwise, the Timer would never be garbage-collected and this
* thread would never go away.
*/
private TaskQueue queue;
TimerThread(TaskQueue queue) {
this.queue = queue;
}
public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
}
/**
* The main timer loop. (See class comment.)
*/
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// Wait for queue to become non-empty
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // Queue is empty and will forever remain; die
// Queue nonempty; look at first evt and do the right thing
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)) {
if (task.period == 0) { // Non-repeating, remove
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // Task hasn't yet fired; wait
queue.wait(executionTime - currentTime);
}
if (taskFired) // Task fired; run it, holding no locks
task.run();
} catch(InterruptedException e) {
}
}
}
}
主要逻辑就位于TimerThread类中,可以看到TimerThread的mainLoop方法中,主逻辑被while(true)死循环包裹,从队列中依次取出一个任务执行,这个主逻辑虽然被try/cache包裹,但是只会捕获InterruptedException 异常,一旦出现其他异常,mainLoop方法就会退出,接着TimerThread的run方法也会退出,这也就意味着这个线程因异常而停止运行了,那么Timer定时器也就没有用了。
总结
- Timer定时器单线程执行,执行时间长的任务会阻塞其他任务,即使已经到了调度时间。
- Timer定时器调度的任务如果抛出异常,TimerThread线程就会退出,Timer定时器也就没用了。
- Timer定时器比较古老,现在用的比较少了,权当了解。
最后
以上就是烂漫黑夜为你收集整理的定时任务之Timer定时器的全部内容,希望文章能够帮你解决定时任务之Timer定时器所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复