做项目很多时候会用到定时任务,比如在深夜,流量较小的时候,做一些统计工作。早上定时发送邮件,更新数据库等。

这里可以用Java的Timer或线程池实现。

Timer可以实现,不过Timer存在一些问题。他起一个单线程,如果有异常产生,线程将退出,整个定时任务就失败。

Timer定时任务原理基本理解:单线程 + 最小堆 + 不断轮询

Timer有四种用法

java timer执行完返回结果 java timer 原理_定时任务

四种用法,通过其参数名称也可以了解到具体含义,下面举例子来实现其功能:

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.SimpleTimeZone;
import java.util.TimerTask;

public class MyTimerTask extends TimerTask {

    private String name;
    private Integer count= 0;
    public MyTimerTask(String inputName){
        name=inputName;
    }
    //创建MyTimerTask类,实线其run方法
    @Override
    public void run(){
        if(count<3){
            Calendar calendar =Calendar.getInstance();
            SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println("Current exec time is :"+sf.format(calendar.getTime()));
            System.out.println("Current name is:"+name);
            count++;
        }
        else {
            //结束
            cancel();
            System.out.println("The task is cancel");
        }
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

下面具体调用task几种用法,可以执行查看效果,具体实现功能在注释中有介绍。 

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;

public class MyTimer {
    public static void main(String[] args) {
        //1.创建一个Timer实例
        Timer timer =new Timer();
        //2.创建一个MyTimerTask实例
        MyTimerTask myTimerTask =new MyTimerTask("No.1");
       
        Calendar calendar =Calendar.getInstance();
        SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("Current exec time is :"+sf.format(calendar.getTime()));

        // 这里获得3s后的时间
        calendar.add(Calendar.SECOND,3);
        //用法1: 1.到达指定时间时候或者超过time时间,执行且执行一次task任务
        myTimerTask.setName("schedue1");
        timer.schedule(myTimerTask,calendar.getTime());

        //用法2:2.指定达到该定时时候首次执行task,之后间隔为period毫秒执行一次
        myTimerTask.setName("schedule2");
        timer.schedule(myTimerTask,calendar.getTime(),2000);

        //用法3:3.delay表示执行任务前的时间,执行一次task
        myTimerTask.setName("schedule3");
        timer.schedule(myTimerTask,1000);

        //用法4:4.delay表示执行任务前的时间,执行一次task的时间间隔,类似第二种用法
        myTimerTask.setName("schedule4");
        timer.schedule(myTimerTask,1000,3000);

        //delay表示执行任务前的时间,执行一次task的时间间隔,类似第二种用法
        myTimerTask.setName("scheduleAtFixedRate1");
        //scheduleAtFixedRate用法和schedule用法类似,两种,分别类似2和4两种用法
        timer.scheduleAtFixedRate(myTimerTask,calendar.getTime(),3000);

    }
}

cancel()用在具体MyTaskTimer里,终止的是其中具体事例的task,如果直接调用timer里面的timer.cancel()则终止了所有的task。

这里也可以将MyTaskTimer写成内部类,就不用写两个类了。

scheduleAtFixedRate和schedule的区别

具体场景

schedule

scheduleAtFixedRate

1、首次计划执行时间早于当前时间

按照上一次实际执行完成的时间点来进行计算

按照上一次开始的时间点进行计算,并且为了赶上进度会多次执行任务,在TimerTask的执行中需要考虑同步

2、任务执行所需时间超出任务执行周期间隔

下一次执行时间相对于上一次实际执行完成的时间点,执行时间不断延后

下一次执行时间相对于上一次开始的时间点,执行时间一般不会延后,因此存在并发性

Timer缺陷和使用禁区

1、在管理并发任务时候的缺陷:

Timer仅有一个线程去执行定时任务,如果存在多个任务,且任务时间比较长,则会产生和预期效果不符的情况。

2、处理抛出异常时候的缺陷:

如果TimeTask抛出RuntimeException,Timer就会停止所有任务的执行。

根据其缺陷,我们在使用时候注意,以下情况就不用Timer了,需要用到Quartz:

1、对时效性要求较高的多任务并发作业

2、对复杂的任务的调度