1、例子入手

package pers.growing.test;import java.util.Timer;
import java.util.TimerTask;public class Main {/*** 延迟100ms后,间隔1s打印出:hello world** @param args* @throws InterruptedException*/public static void main(String[] args) throws InterruptedException {Timer t = new Timer();t.scheduleAtFixedRate(new TimerTask() {@Overridepublic void run() {System.out.println("hello world");}}, 100, 1000);}}

结果:

hello world
hello world
hello world
hello world
hello world
hello world
hello world

2、类讲解

TimerTask.java:主要为任务的具体内容。

Timer.java中含有3个类:Timer、TimerThread、TaskQueue。

三者关系为:TaskQueue中存放一些列将要执行的TimerTask,以数组的形式存放,下标约小(注:下标为0不处理,即使用的最小下标为1),则表明优先级越高。

TimerThread为Thread的扩展类,会一直从TaskQueue中获取下标为1的TimerTask进行执行。并根据该TimerTask是否需要重复执行来决定是否放回到TaskQueue中。

Timer用于配置用户期望的任务执行时间、执行次数、执行内容。它内部会配置TimerThread、TaskQueue。

3、源码解读

Timer类下的方法如下:

Timer中涉及到4个成员变量,queue、thread已经在上面介绍过了,对于nextSerialNumber,只是用于命名默认的thread名称使用。threadReaper为了在GC时进行相关处理,后面再介绍。

Timer的构造函数实现大同小异,以Timer(String,boolean)为例:

  public Timer(String name, boolean isDaemon) {thread.setName(name); //设置成员变量的线程名称thread.setDaemon(isDaemon); //该线程是否为守护线程thread.start();//起线程}

schedule()以schedule(TimerTask,long,long)为例:

 public void schedule(TimerTask task, long delay, long period) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");if (period <= 0)throw new IllegalArgumentException("Non-positive period.");sched(task, System.currentTimeMillis()+delay, -period); //最后调用了sched方法}

这一块有困惑的可能是为什么period取了负数?而且所有的schedule(...)方法,最后传给sched中的period都是负的。之后再介绍。

scheduleAtFixedRate()以scheduleAtFixedRate(TimerTask,long,long)为例:

    public void scheduleAtFixedRate(TimerTask task, long delay, long period) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");if (period <= 0)throw new IllegalArgumentException("Non-positive period.");sched(task, System.currentTimeMillis()+delay, period); //也是调用sched方法}

此时会发现schedule与scheduleAtFixedRate似乎区别不大,唯一有区别的是schedule最后传值给sched的period是负数,而scheduleAtFixedRate传的是正数。

在schedule方法的注释上,有这段内容:

     * <p>In fixed-delay execution, each execution is scheduled relative to* the actual execution time of the previous execution.  If an execution* is delayed for any reason (such as garbage collection or other* background activity), subsequent executions will be delayed as well.* In the long run, the frequency of execution will generally be slightly* lower than the reciprocal of the specified period (assuming the system* clock underlying <tt>Object.wait(long)</tt> is accurate).

翻译:如果出现某一次任务的延迟,那么之后的任务都会以period为周期进行延迟。

而scheduleAtFixedRate方法也有对应注释:

     * <p>In fixed-rate execution, each execution is scheduled relative to the* scheduled execution time of the initial execution.  If an execution is* delayed for any reason (such as garbage collection or other background* activity), two or more executions will occur in rapid succession to* "catch up."  In the long run, the frequency of execution will be* exactly the reciprocal of the specified period (assuming the system* clock underlying <tt>Object.wait(long)</tt> is accurate).

翻译:每次的执行都是以初始时计算好的时间为准,如果出现某次任务的延迟,则之后的任务会快速执行,即按计划时间执行。

那么看下Sched()方法实现:

 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; //给TimerTask赋值task.period = period;task.state = TimerTask.SCHEDULED;}queue.add(task);//将TimerTask放到队列中,并进行队列排序if (queue.getMin() == task)//如果队列里恰好下标为1的任务为当前的task,则直接唤醒queue.notify();}}

queue中的add和getMin操作如下:

    void add(TimerTask task) {// Grow backing store if necessaryif (size + 1 == queue.length)queue = Arrays.copyOf(queue, 2*queue.length);queue[++size] = task;fixUp(size);//让task进行排序,排序并不是十分严谨,将nextExecutionTime较小的往前排}private void fixUp(int k) {while (k > 1) {int j = k >> 1;if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)break;TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;k = j;}}TimerTask getMin() { //注意最小的值是从下标为1的获取,queue[0]其实没用到return queue[1];}

数据已经放到queue中了,那么看下是什么时候执行的。在之前Timer的构造函数这块,有一句是:thread.start();说明TimerThread在Timer初始化之后就一直启用着,那看下它的处理。

 public void run() {try {mainLoop(); //主要实现内容} finally {// Someone killed this Thread, behave as if Timer cancelledsynchronized(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//如果队列为空并且是有标志位,则等待。没有标志位的情况为不在需要执行timer了,比如cancel或被gc的时候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 thinglong currentTime, executionTime;//分别是当前时间、理论执行时间task = queue.getMin();//获取就近的tasksynchronized(task.lock) {//如果该task已经被置为cancelled,则将它从队列里面移出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) { // period表示该task是一次性的,用完就移出queue.removeMin();//移出task,这块会有queue的重新排序task.state = TimerTask.EXECUTED;//更新状态为执行中} else {//可重复执行的task操作,将重新计算下次执行时间,并重新排序    //重点,此处解释为什么period分正负:区别schedule方法和scheduleAtFixedRate//如果是负数,则以当前时间为准,往后计算下次执行时间//如果是正数,则以理论时间为准,往后计算下次执行时间queue.rescheduleMin(task.period<0 ? currentTime   - task.period: executionTime + task.period);}}}if (!taskFired) // 如果还没到任务执行时间就处于等待queue.wait(executionTime - currentTime);}if (taskFired)  // 到执行时间了//执行task中的run方法,而不是start方法,所以并不是另起一个线程进行操作task.run();} catch(InterruptedException e) {//如果是不能捕获的异常,就会有风险了}}}

关于queue中的removeMin()和rescheduleMin()方法如下:

    void removeMin() {//前两行赋值可能会将queue乱序,所以才会有fixDown重新排序queue[1] = queue[size];queue[size--] = null;  // Drop extra reference to prevent memory leakfixDown(1);}//因为取的时候也是取下标为1的task进行操作,所以此次也是将下标为1的task重新赋时间,并排序void rescheduleMin(long newTime) {queue[1].nextExecutionTime = newTime;fixDown(1);}//和fixUp方法类似,该排序单独看也并非严谨,但由于每次操作都会经历,所以应该是准的吧!private void fixDown(int k) {int j;while ((j = k << 1) <= size && j > 0) {if (j < size &&queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)j++; // j indexes smallest kidif (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)break;TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;k = j;}}

当timerTask调用了timerTask.cancel()操作时,也可以人为的将它从Timer队列中清除掉,方法如下:

 public int purge() {int result = 0;synchronized(queue) {for (int i = queue.size(); i > 0; i--) {if (queue.get(i).state == TimerTask.CANCELLED) {queue.quickRemove(i); //由于i取值时必然大于0,所以肯定能够找到正常的数据result++;}}if (result != 0)queue.heapify();//重新排序}return result;}

queue中的quickRemove和heapify方法:

    void quickRemove(int i) { //只要简单赋值就行了,最后排序交给heapify()assert i <= size;queue[i] = queue[size];queue[size--] = null;  // Drop extra ref to prevent memory leak}void heapify() {for (int i = size/2; i >= 1; i--)fixDown(i); //在前面的篇幅中介绍过了}

当然如果Timer也可以人为的取消,不在继续定时任务。其方法如下:

   public void cancel() {synchronized(queue) {thread.newTasksMayBeScheduled = false;queue.clear(); //会将队列中的所有的task赋值为nullqueue.notify();  // 通知thread中的mainLoop进行break操作}}

综上,其实大部分流程就屡清楚了。顺带介绍下Timer中的threadReaper。代码如下:

   /*** This object causes the timer's task execution thread to exit* gracefully when there are no live references to the Timer object and no* tasks in the timer queue.  It is used in preference to a finalizer on* Timer as such a finalizer would be susceptible to a subclass's* finalizer forgetting to call it.*/private final Object threadReaper = new Object() {protected void finalize() throws Throwable {synchronized(queue) {thread.newTasksMayBeScheduled = false;queue.notify(); // In case queue is empty.}}};

重写了finalize方法,该方法为GC时候调用,主要使Timer中的thread能够优雅退出。

4、总结

优势:可以实现比较轻量级的定时任务,而且很方便

劣势:

①由于Timer只是创建了一个thread去执行queue中的task,那么就可能会出现上一个任务执行延迟了,会影响到下一个定时任务。

②在TimerThread#mainloop中,也可看到,如果抛出了InterruptedException之外无法捕获到的异常时,mainloop就会中断,Timer也就无法使用了。

java定时器-Timer和TimerTask详解相关推荐

  1. java timer和timertask_java定时器Timer和TimerTask详解

    目录结构: Timer和TimerTask 一个Timer调度的例子 如何终止Timer线程 关于cancle方式终止线程 反复执行一个任务 schedule VS. scheduleAtFixedR ...

  2. JDK中的Timer和TimerTask详解 目录结构: Timer和TimerTask 一个Timer调度的例子 如何终止Timer线程 关于cancle方式终止线程 反复执行一个任务 sche

    JDK中的Timer和TimerTask详解 目录结构: Timer和TimerTask 一个Timer调度的例子 如何终止Timer线程 关于cancle方式终止线程 反复执行一个任务 schedu ...

  3. Timer和TimerTask详解

    1.概览 Timer是一种定时器工具,用来在一个后台线程计划执行指定任务.它可以计划执行一个任务一次或反复多次. TimerTask一个抽象类,它的子类代表一个可以被Timer计划的任务. 简单的一个 ...

  4. java的timertask_JavaTimer和TimerTask详解

    1.概览 Timer是一种定时器工具,用来在一个后台线程计划执行指定任务.它可以计划执行一个任务一次或反复多次. TimerTask一个抽象类,它的子类代表一个可以被Timer计划的任务. 简单的一个 ...

  5. JAVA 定时器的Cron表达式详解

    spring中用到的定时任务,一般用到的有Timer()和Schedule Cron表达式一般是程序的定时任务中所要起的..我们用的springboot中的@Schedule中,启动类中添加enabl ...

  6. Java定时器的cron设置详解Quartz

    这些星号由左到右按顺序代表 : * * * * * * * 格式: [秒] [分] [小时] [日] [月] [周] [年] 序号 说明   是否必填  允许填写的值         允许的通配符 1 ...

  7. 【转载】Java定时器的cron设置详解

    转自:https://blog.csdn.net/win7system/article/details/77869112 这些星号由左到右按顺序代表 : * * * * * * * 格式: [秒] [ ...

  8. Java 中Timer和TimerTask 定时器和定时任务使用的例子

    转载自  Java 中Timer和TimerTask 定时器和定时任务使用的例子 这两个类使用起来非常方便,可以完成我们对定时器的绝大多数需求 Timer类是用来执行任务的类,它接受一个TimerTa ...

  9. Java定时器Timer

    Java定时器Timer 在JDK库中,Timer类主要负责计划任务的功能,也就是在指定的时开始执行某一个任务.Timer类的主要作用就是设置计划任务,但封装任务的类却是TimerTask类,执行计划 ...

最新文章

  1. 在eclipse里jsp编译后的java和class文件的位置
  2. 《构建之法》第6 - 7章
  3. 如何高效学习java呢?
  4. NPER用计算机怎么算,计算机财务管理第三章详解.doc
  5. windowbuilder安装
  6. 昨天做的事情和今天需要做的事情
  7. 界面控件包Essential Studio for Windows Forms 2017 v3发布丨附下载
  8. frameset的一些操作
  9. zendframework Form表单美化
  10. mtouch 3d gis,m3dgis,mtgis,mgis 多点触摸三维电子沙盘可视化交互系统教程第17课
  11. Tayga NAT64 IPv6与IPv4互访解决方案
  12. 巩固大一,大二知识 练习计划
  13. matlab geoshow 地质,Matlab 绘制三维立体图(以地质异常体为例)
  14. 【noHandlerFound(DispatcherServlet.java:1278)和No mapping for GET】SpringMVC 404和中文乱码问题和解决方案记录
  15. vue自定义指令基础
  16. torch.flatten、np.flatten 详解
  17. Android 卸载监听详解
  18. SAE J1850 汽车总线协议 VPW 物理层驱动程序在STM32芯片上的实现
  19. win10使用腾讯会议软件没声音怎么解决
  20. 中控考勤机通过公网添加入异地中控系统

热门文章

  1. pinyin4j汉语拼音库的使用
  2. 电脑使用技巧(卸载腾讯电脑管家之后系统崩溃)
  3. HTML+CSS - 导航栏下拉菜单
  4. 电阻/电抗/阻抗/电导/电纳/导纳
  5. 有一门课不及格的学生
  6. 启用国行Lumia 830/930的联通4G网络
  7. R语言计算方差分析的F值和P值
  8. 汽车车身电子单元的通用测试工装ETest及其工作方法
  9. Ardiuno 太极创客学习记录111
  10. 【备战美赛】重要!2023年美赛官方发布最新通知