设为首页 加入收藏

TOP

Java Timer(定时调用、实现固定时间执行)(一)
2015-08-31 21:24:03 来源: 作者: 【 】 浏览:78
Tags:Java Timer 定时 调用 实现 固定 时间 执行

  最近需要用到定时调用的功能。可以通过Java的Timer类来进行定时调用,下面是有关Timer的一些相关知识。


  其实就Timer来讲就是一个调度器,而TimerTask呢只是一个实现了run方法的一个类,而具体的TimerTask需要由你自己来实现,例如这样:


Timer timer = new Timer();
timer.schedule(new TimerTask() {
? ? ? ? public void run() {
? ? ? ? ? ? System.out.println("11232");
? ? ? ? }
}, 200000 , 1000);


  这里直接实现一个TimerTask(当然,你可以实现多个TimerTask,多个TimerTask可以被一个Timer会被分配到多个 Timer中被调度,后面会说到Timer的实现机制就是说内部的调度机制),然后编写run方法,20s后开始执行,每秒执行一次,当然你通过一个 timer对象来操作多个timerTask,其实timerTask本身没什么意义,只是和timer集合操作的一个对象,实现它就必然有对应的run 方法,以被调用,他甚至于根本不需要实现Runnable,因为这样往往混淆视听了,为什么呢?也是本文要说的重点。
  在说到timer的原理时,我们先看看Timer里面的一些常见方法:


1、这个方法是调度一个task,经过delay(ms)后开始进行调度,仅仅调度一次。


? ? public void schedule(TimerTask task, long delay)


2、在指定的时间点time上调度一次。


public void schedule(TimerTask task, Date time)


3、这个方法是调度一个task,在delay(ms)后开始调度,每次调度完后,最少等待period(ms)后才开始调度。


public void schedule(TimerTask task, long delay, long period)


4、和上一个方法类似,唯一的区别就是传入的第二个参数为第一次调度的时间。


public void schedule(TimerTask task, Date firstTime, long period)


5、调度一个task,在delay(ms)后开始调度,然后每经过period(ms)再次调度,貌似和方法:schedule是一样的,其实不然,后面你会根据源码看到,schedule在计算下一次执行的时间的时候,是通过当前时间(在任务执行前得到) + 时间片,而scheduleAtFixedRate方法是通过当前需要执行的时间(也就是计算出现在应该执行的时间)+ 时间片,前者是运行的实际时间,而后者是理论时间点,例如:schedule时间片是5s,那么理论上会在5、10、15、20这些时间片被调度,但是如果由于某些CPU征用导致未被调度,假如等到第8s才被第一次调度,那么schedule方法计算出来的下一次时间应该是第13s而不是第10s,这样有可能下次就越到20s后而被少调度一次或多次,而scheduleAtFixedRate方法就是每次理论计算出下一次需要调度的时间用以排序,若第8s被调度,那么计算出应该是第10s,所以它距离当前时间是2s,那么再调度队列排序中,会被优先调度,那么就尽量减少漏掉调度的情况。


public void scheduleAtFixedRate(TimerTask task, long delay, long period)


6、方法同上,唯一的区别就是第一次调度时间设置为一个Date时间,而不是当前时间的一个时间片,我们在源码中会详细说明这些内容。


public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period)


--------------------------------------------------------------------------------


源码部分


首先看Timer的构造方法有几种:


构造方法1:无参构造方法,简单通过Tiemer为前缀构造一个线程名称:


public Timer() {
? ? this("Timer-" + serialNumber());
}


?  创建的线程不为主线程,则主线程结束后,timer自动结束,而无需使用cancel来完成对timer的结束。


构造方法2:传入了是否为后台线程,后台线程当且仅当进程结束时,自动注销掉。


public Timer(boolean isDaemon) {
? ? this("Timer-" + serialNumber(), isDaemon);
}


?  另外两个构造方法负责传入名称和将timer启动:


public Timer(String name, boolean isDaemon) {
? ? ? thread.setName(name);
? ? ? thread.setDaemon(isDaemon);
? ? ? thread.start();
? }


?  这里有一个thread,这个thread很明显是一个线程,被包装在了Timer类中,我们看下这个thread的定义是:


private TimerThread thread = new TimerThread(queue);


  而定义TimerThread部分的是:


而定义TimerThread部分的是:


?  看到这里知道了,Timer内部包装了一个线程,用来做独立于外部线程的调度,而TimerThread是一个default类型的,默认情况下是引用不到的,是被Timer自己所使用的。


  接下来看下有那些属性


  除了上面提到的thread,还有一个很重要的属性是:


private TaskQueue queue = new TaskQueue();


?  看名字就知道是一个队列,队列里面可以先猜猜看是什么,那么大概应该是我要调度的任务吧,先记录下了,接下来继续向下看:


  里面还有一个属性是:threadReaper, 它是Object类型,只是重写了finalize方法而已,是为了垃圾回收的时候,将相应的信息回收掉,做GC的回补,也就是当timer线程由于某种 原因死掉了,而未被cancel,里面的队列中的信息需要清空掉,不过我们通常是不会考虑这个方法的,所以知道java写这个方法是干什么的就行了。


  接下来看调度方法的实现:


  对于上面6个调度方法,我们不做一一列举,为什么等下你就知道了:
  来看下方法:


public void schedule(TimerTask task, long delay)


?  的源码如下:


1 public void schedule(TimerTask task, long delay) {
2? ? ? ? if (delay < 0)
3? ? ? ? ? ? throw new IllegalArgumentException("Negative delay.");
4? ? ? ? sched(task, System.currentTimeMillis()+delay, 0);
5? ? }


?  这里调用了另一个方法,将task传入,第一个参数传入System.currentTimeMillis()+delay可见为第一次需要执行的时间的 时间点了(如果传入Date,就是对象.getTime()即可,所以传入Date的几个方法就不用多说了),而第三个参数传入了0,这里可以猜下要么是 时间片,要么是

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇Spring 容器加载完成后执行某个方.. 下一篇PHP的MySQLi函数库的使用 及 表单..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: