做项目很多时候会用到定时任务,比如在深夜,流量较小的时候,做一些统计工作。早上定时发送邮件,更新数据库等。
这里可以用Java的Timer或线程池实现。
Timer可以实现,不过Timer存在一些问题。他起一个单线程,如果有异常产生,线程将退出,整个定时任务就失败。
Timer定时任务原理基本理解:单线程 + 最小堆 + 不断轮询
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、对复杂的任务的调度