定时任务定义
定时任务调度:基于给定的时间点、给定的时间间隔或者给定的执行次数自动执行的任务。
JDK定时任务Timer
1.JDK定时任务工具类Timer
Timer:有且仅有一个后台线程对多个业务线程进行定时、定频率的调度;Timer定时任务(schedule)的四种用法:后台线程Timer——业务线程TimerTask
- schedule(task,time)——在时间等于或者超过time的时候执行且仅执行一次task;(schedule1代码)
- schedule(task,time,period)——时间等于或者超过timer时首次执行task,之后每隔period毫秒重复执行一次task;(schedule2代码)
- schedule(task,delay)——等待delay毫秒后执行且仅执行一次task;(schedule3代码)
- schedule(task,delay,period)——等待delay毫秒后首次执行task,之后每隔period毫秒重复执行一次task;(schedule4代码)
- scheduleAtFixedRate(task,time,period)——时间等于或者超过time时首次执行task,之后每隔period毫秒重复执行一次task;(atFixedRate1代码)
- scheduleAtFixedRate(task,delay,period)——等待delay毫秒后首次执行task,之后每隔period毫秒重复执行一次task;(atFixedRate2代码)
2.创建TimerTask业务线程实现类JdkTimerTask:
package com.luna.timer.jdk; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.TimerTask; public class JdkTimerTask extends TimerTask{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public JdkTimerTask() { } @Override public void run() { 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 exec name is:"+name); } }
3.创建后台线程Timer类的实现JDKTimer:
package com.luna.timer.jdk; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Timer; public class JdkTimer { public static void main(String[] args) { Timer timer = new Timer(); JdkTimerTask timerTask = new JdkTimerTask(); 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())); //获取当前时间3秒之后的时间,各个任务独立运行避免相互影响 calendar.add(Calendar.SECOND, 3); // timerTask.setName("schedule1"); // //在时间等于或者超过time的时候执行且仅执行一次task // timer.schedule(timerTask, calendar.getTime()); // timerTask.setName("schedule2"); // //时间等于或者超过timer时首次执行task,之后每隔period毫秒重复执行一次task // timer.schedule(timerTask, calendar.getTime(),2000); // timerTask.setName("schedule3"); // //等待delay毫秒后执行且仅执行一次task // timer.schedule(timerTask, 1000); // System.out.println("最近发生此任务执行安排的时间为:"+sf.format(timerTask.scheduledExecutionTime())); // timerTask.setName("schedule4"); // //等待delay毫秒后首次执行task,之后每隔period毫秒重复执行一次task // timer.schedule(timerTask, 3000, 2000); // timerTask.setName("atFixedRate1"); // //时间等于或者超过time时首次执行task,之后每隔period毫秒重复执行一次task // timer.scheduleAtFixedRate(timerTask, calendar.getTime(),2000); timerTask.setName("atFixedRate2"); //等待delay毫秒后首次执行task,之后每隔period毫秒重复执行一次task timer.scheduleAtFixedRate(timerTask, 3000,2000); } }
4.Timer其他函数
- TimerTask类cancel()——取消当前TimerTask里的循环执行的任务;
package com.luna.timer.jdk; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.TimerTask; public class JdkCancelTimerTask extends TimerTask{ private String name; private Integer count = 0; public String getName() { return name; } public void setName(String name) { this.name = name; } public JdkCancelTimerTask() { } @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 exec name is:"+name); count++; }else{ cancel(); System.out.println("Task cancel!"); } } }
- TimerTask类scheduleExecutionTime()——返回此任务最近实际执行的已安排执行的时间;返回值为最近发生此任务执行安排的时间(Long);
- Timer类cancel()——终止此计时器,丢弃所有当前已安排的任务;
package com.luna.timer.jdk; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; public class JdkCancelTimer { public static void main(String[] args) throws InterruptedException { Timer timer = new Timer(); JdkTimerTask timerTask1 = new JdkTimerTask(); timerTask1.setName("timerTask1"); JdkTimerTask timerTask2 = new JdkTimerTask(); timerTask2.setName("timerTask2"); Date startTime = new Date(); SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("Start time is:"+sf.format(startTime)); timer.schedule(timerTask1, 3000,2000); timer.schedule(timerTask2, 1000,2000); Thread.sleep(5000); Date cancelTime = new Date(); System.out.println("Cancel time is:"+sf.format(cancelTime)); timer.cancel(); System.out.println("Tasks all canceled!"); } }
- purge()——从此计时器的任务队列中移除所有已取消的任务,返回值为队列中移除的任务数;
package com.luna.timer.jdk; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; public class JdkPurgeTimer { public static void main(String[] args) throws InterruptedException { Timer timer = new Timer(); JdkTimerTask timerTask1 = new JdkTimerTask(); timerTask1.setName("timerTask1"); JdkTimerTask timerTask2 = new JdkTimerTask(); timerTask2.setName("timerTask2"); Date startTime = new Date(); SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("Start time is:"+sf.format(startTime)); timer.schedule(timerTask1, 3000,2000); timer.schedule(timerTask2, 1000,2000); System.out.println("当前已取消的任务数为:"+timer.purge()); Thread.sleep(2000); Date cancelTime = new Date(); System.out.println("Cancel time is:"+sf.format(cancelTime)); timerTask2.cancel(); System.out.println("当前已取消的任务数为:"+timer.purge()); } }
5.schedule与scheduleAtFixedRate的区别
- 首次计划执行的时间早于当前的时间
- "fixed-delay"——如果第一次执行时间被delay了,随后的执行时间按照上一次实际执行完成的时间进行计算;
package com.luna.timer.jdk; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Timer; import java.util.TimerTask; public class DifferTest { public static void main(String[] args) { final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Calendar calendar = Calendar.getInstance(); System.out.println("当前时间为:"+sf.format(calendar.getTime())); calendar.add(Calendar.SECOND, -6); Timer timer = new Timer(); //第一次执行时间为6秒前,之后每隔两秒钟执行一次 timer.schedule(new TimerTask() { @Override public void run() { System.out.println("当前计划执行时间为:"+sf.format(scheduledExecutionTime())); System.out.println("定时任务正在被执行!"); } },calendar.getTime(), 2000); } }
- "fixed-rate"——如果第一次执行时间被delay了,随后的执行时间按照上一次开始的时间点进行计算并且为了赶上进度会多次执行任务,因此TimerTask中的执行体需要考虑同步;
package com.luna.timer.jdk; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Timer; import java.util.TimerTask; public class DifferTest { public static void main(String[] args) { final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Calendar calendar = Calendar.getInstance(); System.out.println("当前时间为:"+sf.format(calendar.getTime())); calendar.add(Calendar.SECOND, -6); Timer timer = new Timer(); //第一次执行时间为6秒前,之后每隔两秒钟执行一次 timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { System.out.println("当前计划执行时间为:"+sf.format(scheduledExecutionTime())); System.out.println("定时任务正在被执行!"); } },calendar.getTime(), 2000); } }
- 任务执行所需时间超出任务的执行周期间隔
- schedule()——下一次执行时间相对于上一次实际执行完成的时间点,因此执行时间会不断延后;
package com.luna.timer.jdk; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Timer; import java.util.TimerTask; public class DifferTest { public static void main(String[] args) { final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Calendar calendar = Calendar.getInstance(); System.out.println("当前时间为:"+sf.format(calendar.getTime())); Timer timer = new Timer(); //第一次执行时间为6秒前,之后每隔两秒钟执行一次 timer.schedule(new TimerTask() { @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("当前计划执行时间为:"+sf.format(scheduledExecutionTime())); System.out.println("定时任务正在被执行!"); } },calendar.getTime(), 2000); } }
- scheduleAtFixedRate()——下一次执行时间相对于上一次开始的时间点,因此执行时间一般不会延后,因此存在并发性;
package com.luna.timer.jdk; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Timer; import java.util.TimerTask; public class DifferTest { public static void main(String[] args) { final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Calendar calendar = Calendar.getInstance(); System.out.println("当前时间为:"+sf.format(calendar.getTime())); Timer timer = new Timer(); //第一次执行时间为6秒前,之后每隔两秒钟执行一次 timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("当前计划执行时间为:"+sf.format(scheduledExecutionTime())); System.out.println("定时任务正在被执行!"); } },calendar.getTime(), 2000); } }
5.灌水机器人和跳舞机器人
灌水机器人每隔1秒灌一次水到木桶中,跳舞机器人每隔两秒跳一次舞,当木桶水满了时(水容量达到5L)灌水机器人停止灌水,2秒后跳舞机器人停止跳舞。灌水机器人代码如下:
package com.luna.timer.jdk.robot; import java.util.Timer; import java.util.TimerTask; public class WaterRobot extends TimerTask{ private Timer timer; public WaterRobot(Timer timer) { this.timer = timer; } //最大容量为5L private Integer bucketCapacity = 0; @Override public void run() { //灌水直至桶满为止 if(bucketCapacity<5){ System.out.println("灌水机器人灌水一升到木桶中!"); bucketCapacity++; }else{ //水满之后就停止执行 cancel(); System.out.println("灌水机器人停止执行!"); System.out.println("取消的任务数目为:"+timer.purge()); System.out.println("当前桶中的水容量为:"+bucketCapacity+"L"); try { //等待两秒终止timer里面的所有内容 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } timer.cancel(); } } }
跳舞机器人代码如下:
package com.luna.timer.jdk.robot; import java.text.SimpleDateFormat; import java.util.TimerTask; public class DancingRobot extends TimerTask{ @Override public void run() { SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("计划任务执行时间是:"+sf.format(scheduledExecutionTime())); System.out.println("跳舞机器人在愉快的跳舞。。。"); } }
测试代码如下:
package com.luna.timer.jdk.robot; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Timer; public class Executor { public static void main(String[] args) { Timer timer = new Timer(); Calendar calendar = Calendar.getInstance(); SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("计划任务执行时间是:"+sf.format(calendar.getTime())); DancingRobot dancingRobot = new DancingRobot(); WaterRobot waterRobot = new WaterRobot(timer); timer.schedule(dancingRobot, calendar.getTime(),2000); timer.scheduleAtFixedRate(waterRobot, calendar.getTime(),1000); } }
定时任务Timer总结
- 管理并发任务的缺陷:Timer有且仅有一个线程去执行定时任务,如果存在多个任务,且任务时间过长,会导致执行效果与预期不符;
- 如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行;
- Timer不支持对时效性要求较高的多任务并发作业,也不支持复杂的任务调度(可能有异常、每周三执行一次任务等场景)那么大哥Quartz就要出马了。