Java在1.3版本号引入了Timerjava工具,它是一个历史悠久的计时器,配搭TimerTask和TaskQueue一起应用。从Java5逐渐在并分包中引入了另一个计时器
ScheduledThreadPoolExecutor,它对Timer进行了许多改善并带来了大量的专用工具,可以觉得是对Timer的替代。
那为什么还需要详细介绍Timerjava工具呢?根据掌握Timer的基本功能和它后面的基本原理,有利于大家更快的比照掌握
ScheduledThreadPoolExecutor,与此同时ScheduledThreadPoolExecutor的一些改善观念在大家日常的编号工作中中也可以参考。
关键成员变量
Timer中采用的主要是2个成员变量:
- TaskQueue:一个依照時间优先选择排序的序列,这儿的時间是每一个定时任务下一次执行的ms数(相对性于1970年1月1日来讲)
- TimerThread:对TaskQueue里边的定时任务开展编辑和开启执行,它是一个内部结构不断循环的进程。
//依据時间开展优先选择排序的序列
private final TaskQueue queue = new TaskQueue();
//交易进程,对queue中的定时任务开展编辑和执行
private final TimerThread thread = new TimerThread(queue);
//构造方法
public Timer(String name) {
thread.setName(name);
thread.start();
}
按时作用
Timer给予了三种按时方式:
- 一次性每日任务
- 依照确定的延迟时间执行(fixed delay)
- 依照确定的周期时间执行(fixed rate)
第一种比较好了解,即每日任务只执行一次;对于第一种,Timer给予了下列2个方式:
//在现在时间往后面delay个ms逐渐执行
public void schedule(TimerTask task, long delay) {...}
//在规定的time时间点执行
public void schedule(TimerTask task, Date time) {...}
第二种Fixed Delay方式也给予了下列2个方式
//从现在时间逐渐delay个ms数逐渐按时执行,周期时间是period个ms数
public void schedule(TimerTask task, long delay, long period) {...}
////从特定的firstTime逐渐按时执行,往后面每一次执行的期限是period个ms数
public void schedule(TimerTask task, Date firstTime, long period){...}
它的工作方式是:
第一次执行的时长将根据规定的时间点执行(假如这时TimerThread没有在执行别的每日任务),若有别的每日任务在执行,那么就要直到别的每日任务执行进行才可以执行。
从第二次逐渐,每一次每日任务的执行时间是上一次任务逐渐执行的時间再加上特定的periodms数。
怎样看待呢,大家或是看编码
public static void main(String[] args) {
TimerTask task1 = new DemoTimerTask(\"Task1\");
TimerTask task2 = new DemoTimerTask(\"Task2\");
Timer timer = new Timer();
timer.schedule(task1, 1000, 5000);
timer.schedule(task2, 1000, 5000);
}
static class DemoTimerTask extends TimerTask {
private String taskName;
private DateFormat df = new SimpleDateFormat(\"HH:mm:ss---\");
public DemoTimerTask(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
System.out.println(df.format(new Date()) taskName \" is working.\");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(df.format(new Date()) taskName \" finished work.\");
}
}
task1和task2是几乎与此同时执行的2个每日任务,并且执行时间全是2秒左右,假如这个时候大家把第六行注掉不执行,我们将获得如下所示結果(和第三种Fixed Rate方式結果同样):
13:42:58---Task1 is working.
13:43:00---Task1 finished work.
13:43:03---Task1 is working.
13:43:05---Task1 finished work.
13:43:08---Task1 is working.
13:43:10---Task1 finished work.
假如开启第六行,大家再看看2个每日任务的执行状况。我们都是期待2个每日任务可以与此同时执行,可是Task2是在Task1执行进行后才逐渐执行(缘故是TimerThread是单核的,每一个定时任务的执行也在该进程内进行,当好几个每日任务与此同时必须执行时,只有是堵塞了),进而造成Task2第二次执行的时间是它上一次执行的時间(13:43:57)再加上5秒左右(13:44:02)。
13:43:55---Task1 is working.
13:43:57---Task1 finished work.
13:43:57---Task2 is working.
13:43:59---Task2 finished work.
13:44:00---Task1 is working.
13:44:02---Task1 finished work.
13:44:02---Task2 is working.
13:44:04---Task2 finished work.
那假如这时也有个Task3也是相同的时间点和间距执行会怎么样?
结果是:也将先后排长队,执行的時间依靠2个要素:
1.之前执行的時间
2.期待执行的时间点上是否有别的每日任务在执行,有则只有排长队了
大家接着看下第三种Fixed Rate方式,大家将里面的编码稍加改动:
public static void main(String[] args) {
TimerTask task1 = new DemoTimerTask(\"Task1\");
TimerTask task2 = new DemoTimerTask(\"Task2\");
Timer timer = new Timer();
timer.scheduleAtFixedRate(task1, 1000, 5000);
timer.scheduleAtFixedRate(task2, 1000, 5000);
}
static class DemoTimerTask extends TimerTask {
private String taskName;
private DateFormat df = new SimpleDateFormat(\"HH:mm:ss---\");
public DemoTimerTask(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
System.out.println(df.format(new Date()) taskName \" is working.\");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(df.format(new Date()) taskName \" finished work.\");
}
}
Task1和Task2或是在同样的时间点,依照同样的周期时间按时执行每日任务,大家期待Task1可以每5秒按时执行每日任务,期待的时间点是:14:21:47-14:21:52-14:21:57-14:22:02-14:22:07,事实上它可以更替着按时执行,缘故是Task2也会按时执行,而且对TaskQueue的锁她们是轮换着拿的(这一在下面剖析TimerThread源代码的过程中会讲到)
14:21:47---Task1 is working.
14:21:49---Task1 finished work.
14:21:49---Task2 is working.
14:21:51---Task2 finished work.
14:21:52---Task2 is working.
14:21:54---Task2 finished work.
14:21:54---Task1 is working.
14:21:56---Task1 finished work.
14:21:57---Task1 is working.
14:21:59---Task1 finished work.
14:21:59---Task2 is working.
14:22:01---Task2 finished work.
TimerThread
上边大家具体讲了Timer的一些关键源码及定期方式,下边大家来剖析下支撑Timer的定时任务线程TimerThread。
TimerThread大概流程表如下所示:
TimerThread步骤
源码表述如下所示:
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// 假如queue里边沒有要实行的每日任务,则挂起TimerThread线程
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
// 假如TimerThread被激话,queue里边或是沒有每日任务,则详细介绍该线程的不断循环,不会再接纳新每日任务
if (queue.isEmpty())
break;
long currentTime, executionTime;
// 获得queue序列里边下一个要实行的每日任务(依据时间排序,也就是下面近期要实行的每日任务)
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;
// taskFired表明是不是必须立即实行线程,当task的下一次实行時间抵达现在时间点时为true
if (taskFired = (executionTime<=currentTime)) {
//task.period==0表明这一工作只必须实行一次,这儿就从queue里边删除了
if (task.period == 0) {
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
//对于task.period不等于0的每日任务,则测算它的下一次实行时间点
//task.period<0表明是fixed delay方式的每日任务
//task.period>0表明是fixed rate方式的每日任务
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime task.period);
}
}
}
// 假如每日任务的下一次实行時间都还没抵达,则挂起TimerThread线程executionTime - currentTimems数,抵达实行时间点再全自动激话
if (!taskFired)
queue.wait(executionTime - currentTime);
}
// 假如每日任务的下一次实行时间到了,则执行任务
// 留意:这儿每日任务实行沒有另起线程,或是在TimerThread线程实行的,因此当有每日任务在与此同时实行的时候会发生堵塞
if (taskFired)
// 这儿沒有try catch出现异常,当TimerTask抛出异常会致使全部TimerThread跳出循环,进而造成Timer无效
task.run();
} catch(InterruptedException e) {
}
}
}
结果
根据以上的剖析,我们可以得到下列结果:
- Timer适用三种策略的定时任务(一次性每日任务,Fixed Delay方式,Fixed Rate模式)
- Timer中的TimerThread是单线程方式,因而造成全部定时任务不可以与此同时实行,很有可能会发生延迟时间
- TimerThread中并没解决好每日任务的出现异常,因而每一个TimerTask的建立务必自身try catch避免出现异常抛出去,造成Timer整体失效
Demo编码部位
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。