java 定时任务调度工具详解
1.什么是定时任务调度?
基于给定的时间点,给定的时间间隔或者给定的执行次数 自动执行的任务
2.Java中两款比较流行的调度工具
Timer 60%
Quartz
3.Timer和Quartz的区别?
Timer、Quartz的使用需要具备Java基础知识
Quartz,sping的合体需要具备Spring基础知识
有且仅有一个后台线程对多个业务线程进行定时定频率的调度
Timer的构件
Timer(后台线程) --->定时调用TimerTask(业务线程)
Timer工具类详解
Timer -------> TimerThread
-queue:TaskQueue +run():void
-thread:TimerThread -mainLoop():void
+Timer(isDaemon:boolean):void
+Timer(name:String):void
+schedule(task:TimeTask,time:Date):void
|
v
TaskQueue
-queue:TimeTask[] ------>TimeTask (业务线程)
+run():void
schedule(task,time) 在时间等于或超过time的时候执行,仅执行一次task。
task-所安排的任务
time-执行任务的时间
例如:
task-所安排的任务
time-首次执行的时间
task-所安排的任务
delay-执行任务前的延时时间,单位毫秒
例如:
task-所安排的任务
delay-执行任务前的延时时间,单位毫秒
period-执行一次task的时间间隔,单位是毫秒
例如:
task-所安排的任务
time-执行任务的时间
period-执行一次task的时间间隔,单位是毫秒
例如:
task-所安排的任务
delay-执行任务前的延时时间,单位毫秒
period-执行一次task的时间间隔,单位是毫秒
例如:
cancel() 作用:取消当前TimerTask里的任务
例如:
cancel() 作用:终止此计时器,丢弃所有当前已安排的任务----群体秒杀
purge() 从此计时器的任务队列中移除所有已取消的任务,返回值为从队列中移除的任务数
例如:
首次计划执行的时间早于当前时间
1.schedule方法
"fixed-delay" 如果第一次执行时间被delay了,随后的执行时间按照上一次实际执行的时间点进行计算
scheduleAtFixedRate方法
"fixed-delay" 如果第一次执行时间被delay了,随后的执行时间按照上一次开始的时间进行计算,并且为了
赶上进度会多次执行任务,因此TimeTask中的执行体需要考虑同步。
例如:
任务执行所需的时间超出任务周期间隔
1.schedule方法
下一次执行时间相对于上一次实际执行完成的时间点,因此执行时间会不断延后。
2.scheduleAtFixedRate
下一次执行时间相对于上一次开始时间点,因此执行时间一般不会延后,因此存在并发性。
实现两个机器人
第一个机器人会每隔两秒打印最近一次计划执行时间、执行的内容。
第二个机器人模拟往桶里灌水,直到桶里的水慢为止。
灌水机器人执行流程
灌水
|
v
一直工作<-水是否满
|
v
停止工作
跳舞机器人执行流程
跳舞
|
v
一直工作<-是否满
|
v
跳舞两秒 ->停止工作
Timer有且只有一个线程去执行定时任务,如果存在多个任务,且时间过长,会导致执行效果与预期不符
当任务抛出异常的缺陷
如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行。
对复杂任务的调度(抛出异常)
不灵活
1.什么是定时任务调度?
基于给定的时间点,给定的时间间隔或者给定的执行次数 自动执行的任务
2.Java中两款比较流行的调度工具
Timer 60%
Quartz
3.Timer和Quartz的区别?
出身不同
Timer jdk提供,调用方式简单,不需要别jar包支持
Quartz需要引入jar包
能力区别
Timer 能够执行简单的定时任务,如果执行在具体时间执行什么任务,Timer就能搞定
Quartz 每个星期天8点执行任务
底层机制
Timer 只有一个后台线程执行任务
Quartz有线程池,由多个线程执行调度任务
4.前置知识Timer、Quartz的使用需要具备Java基础知识
Quartz,sping的合体需要具备Spring基础知识
一、Timer简介
Timer入门有且仅有一个后台线程对多个业务线程进行定时定频率的调度
Timer的构件
Timer(后台线程) --->定时调用TimerTask(业务线程)
Timer工具类详解
Timer -------> TimerThread
-queue:TaskQueue +run():void
-thread:TimerThread -mainLoop():void
+Timer(isDaemon:boolean):void
+Timer(name:String):void
+schedule(task:TimeTask,time:Date):void
|
v
TaskQueue
-queue:TimeTask[] ------>TimeTask (业务线程)
+run():void
二、Timer实战
public class MyTimeTask extends TimeTask{
private String name;
public MyTimeTask(String inputName){
name = inputName;
}
@Override
public void run(){
//打印当前name的内容
System.out.println("Current exec name is :" + name);
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
public class MyTime{
public static void main(String ... args){
//创建一个Timer实例
Timer timer = new Timer();
//创建一个MyTimerTask实例
MyTimeTask myTimerTask = new MyTimeTask("NO.1");
//通过timer定时定频率调用myTimerTask的业务逻辑
//即第一次执行是在当前时间的两秒钟之后执行,之后每隔一秒钟执行一次
timer.schedule(myTimerTask,2000L,1000L);
}
}
三、Timer的定时调度函数
schedule的四种用法schedule(task,time) 在时间等于或超过time的时候执行,仅执行一次task。
task-所安排的任务
time-执行任务的时间
例如:
@Override
public void run(){
Calender calender = Calender.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("Current exec time is :" + sf.format(calender.getTime());
//打印当前name的内容
System.out.println("Current exec name is :" + name);
}
Calender calender = Calender.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sf.format(calender.getTime());
calender.add(Calender.SECOND,3);
myTimerTask.setName("Schedule1");
timer.schedule(myTimerTask,calender.getTime());
Schedule(task,time,perio) 时间等于或超过time时首次执行task,之后每隔period毫秒重复执行一次task-所安排的任务
time-首次执行的时间
period-执行一次task的时间间隔,单位是毫秒
例如:
myTimerTask.setName("Schedule2");
timer.schedule(myTimerTask,calender.getTime(),2000L);
Schedule(task,delay) 等待delay毫秒之后执行且只执行一次tasktask-所安排的任务
delay-执行任务前的延时时间,单位毫秒
例如:
myTimerTask.setName("Schedule3");
timer.schedule(myTimerTask,2000L);
Schedule(task,delay,period) 等待delay毫秒后首次执行task,之后每隔period毫秒重复执行一次tasktask-所安排的任务
delay-执行任务前的延时时间,单位毫秒
period-执行一次task的时间间隔,单位是毫秒
例如:
myTimerTask.setName("Schedule4");
timer.schedule(myTimerTask,2000L,1000L);
scheduleAtFixedRate的两种用法
scheduleAtFixedRate(task,time,period) 时间等于或超过time时首次执行task,之后每隔period毫秒之后执行tasktask-所安排的任务
time-执行任务的时间
period-执行一次task的时间间隔,单位是毫秒
例如:
myTimerTask.setName("scheduleAtFixedRate1");
timer.scheduleAtFixedRate(myTimerTask,calender.getTime(),2000L);
scheduleAtFixedRate(task,delay,period)task-所安排的任务
delay-执行任务前的延时时间,单位毫秒
period-执行一次task的时间间隔,单位是毫秒
例如:
myTimerTask.setName("scheduleAtFixedRate2");
timer.scheduleAtFixedRate(myTimerTask,2000L,1000L);
四、总结
Timer通过Schedule或者scheduleAtFixedRate函数都能实现对task的一次或多次的定时调度,只需要告诉他执行task的时间和频率就能完成对task的定时定频率调度五、其他重要函数
TimeTask的cancel(),scheduledExecutionTime()cancel() 作用:取消当前TimerTask里的任务
例如:
@Override
public void run(){
if(count<3){
...
}else{
cancel();
System.out.println("Task cancel!");
}}
scheduledExecutionTime() 作用:返回此任务最近实际执行的已安排执行的时间返回值:最近发生此任务执行安排的时间,为long型
例如:
Timer的cancel(),purge()cancel() 作用:终止此计时器,丢弃所有当前已安排的任务----群体秒杀
public class CancelTest{
public static void main(String ... args)throws Exception{
//创建Timer实例
Timer timer = new Timer();
//创建TimerTask实例
MyTimeTask task1 = new MyTimeTask("task1");
MyTimeTask task2 = new MyTimeTask("task2");
//获取当前的执行时间并打印
Calender calender = Calender.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sf.format(calender.getTime());
//task1首次执行距离现在时间3秒后执行,之后每隔2秒执行一次
//task1首次执行距离现在时间1秒后执行,之后每隔2秒执行一次
timer.schedule(task1,3000,2000);
timer.schedule(task2,1000,2000);
//休眠5000
Thread.sleep(5000);
//获取当前的执行时间并打印
Calender calender = Calender.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("cancel time is :" + sf.format(calender.getTime());
//取消所有任务
timer.cancel();
System.out.println("Tasks all canceled!");
}
}
purge() 从此计时器的任务队列中移除所有已取消的任务,返回值为从队列中移除的任务数
例如:
System.out.println("current canceled task number is : " + timer.purge); //0
Thread.sleep(2000);
.....
task2.cancel();
System.out.println("current canceled task number is : " + timer.purge); //1
六、schedule与scheduleAtFixedRate的区别
两种情况看区别首次计划执行的时间早于当前时间
1.schedule方法
"fixed-delay" 如果第一次执行时间被delay了,随后的执行时间按照上一次实际执行的时间点进行计算
public class DiffferenceTest{
public static void main(String ... args){
//规定时间格式
final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//获取当前的具体时间
Calender calender = Calender.getInstance();
System.out.println("Current time is :" + sf.format(calender.getTime()));
//设置成6秒前的时间,若当前时间为2016-12-28 00:00:06
//那么设置之后的时间变成2016-12-28 00:00:00
calender.add(Calender.SECOND,-6);
Timer timer = new Timer();
//第一次执行时间为6秒前,之后每隔两秒钟执行一次
timer.schedule(new TimeTask(){
@Override
public void run(){
//打印当前的计划执行时间
System.out.println(scheduledExecutionTime());
}
},calender.getTime(),2000);
}
}
scheduleAtFixedRate方法
"fixed-delay" 如果第一次执行时间被delay了,随后的执行时间按照上一次开始的时间进行计算,并且为了
赶上进度会多次执行任务,因此TimeTask中的执行体需要考虑同步。
例如:
timer.scheduleAtFixedRate方法(new TimeTask(){
@Override
public void run(){
//打印当前的计划执行时间
System.out.println(scheduledExecutionTime());
}
},calender.getTime(),2000);
任务执行所需的时间超出任务周期间隔
1.schedule方法
下一次执行时间相对于上一次实际执行完成的时间点,因此执行时间会不断延后。
2.scheduleAtFixedRate
下一次执行时间相对于上一次开始时间点,因此执行时间一般不会延后,因此存在并发性。
七、Timer函数的综合应用
通过模拟两个机器人的定时行为来把我们前面所学的主要函数结合起来,让大家加深对这些函数的理解实现两个机器人
第一个机器人会每隔两秒打印最近一次计划执行时间、执行的内容。
第二个机器人模拟往桶里灌水,直到桶里的水慢为止。
灌水机器人执行流程
灌水
|
v
一直工作<-水是否满
|
v
停止工作
跳舞机器人执行流程
跳舞
|
v
一直工作<-是否满
|
v
跳舞两秒 ->停止工作
public class DancingRobot extends TimeTask{
@Override
public void run(){
//获取最近一次任务执行时间,并将其格式化
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("Schedule exec time is :" + sf.format(scheduledExecutionTime()));
System.out.println("Dancing happly");
}
}
public class WaterRobot extends TimeTask{
//最大容量为5
private Integer bucketCapacity = 0;
private Timer timer;
public WaterRobot(Timer timer){
this.timer = timer
}
@Override
public void run(){
//灌水直至桶满
if(bucketCapacity < 5){
System.out.println("Add 1L water");
bucketCapacity++ ;
}else{
//水满之后停止执行
cancel();
System.out.println("The waterRobot has been aborted");
//等待两秒钟,终止timer里面所有内容
Thread.sleep(2000);
timer.cancel();
}
}
}
public class Executor{
public static main(String ... args){
Timer timer = new Timer();
//获取当前时间
Calender calender = Calender.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("current time is :" + sf.format(calender.getTime());
DancingRobot dr = new DancingRobot();
WaterRobot wr = new WaterRobot(timer);
timer.schedule(dr,calender.getTime(),2000);
timer.scheduleAtFixedRate(wr,calender.getTime(),1000);
}
}
八、Timer缺陷
管理并发任务缺陷 只有一个后台线程,不支持任务与任务并发Timer有且只有一个线程去执行定时任务,如果存在多个任务,且时间过长,会导致执行效果与预期不符
public class MyTimerTask extends TimeTask{
private String name;
private long costTime;
public MyTimeTask(String name ,long costTime){
this.name = name;
this.costTime = costTime;
}
@Override
public void run(){
//以yyyy-MM-dd HH:mm:ss的格式打印当前执行时间
//如2016-11-11 00:00:00
Calender calender = Calender.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(name + "'s current exec time is :" +sf.format(calender.getTime()));
try{
Thread.sleep(costTime);
}catch(InterruptedException e){
e.printlnStackTrace();
}
//获取costTime之后的时间
calender = Calender.getInstance();
System.out.println(name + "'s finish time is :" + sf.format(calender.getTime()));
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setCostTime(long costTime){
this.costTime = costTime;
}
public String getCostTime(){
return costTime;
}
}
public class MyTimer{
public static void main(String ... args){
Timer timer = new Timer();
MyTimerTask myTimerTask1 = new MyTimeTask("No.1",2000);
MyTimerTask myTimerTask2 = new MyTimeTask("No.2",2000);
Calender calender = Calender.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("current time is :" + sf.format(calender.getTime()));
timer.schedule(myTimerTask1,calender.getTime());
timer.schedule(myTimerTask2,calender.getTime());
}
}
当任务抛出异常的缺陷
如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行。
九、Timer的使用禁区
对时效性要求较高的多任务并发作业对复杂任务的调度(抛出异常)
不灵活