定时任务就是在指定时间执行程序,或周期性执行计划任务。Java中实现定时任务的方法有不少,本文从从JDK自带的一些方法来实现定时任务的需求。html
Timer和TimerTask能够做为线程实现的第三种方式(前两种详见《Java多线程基础》),JDK1.5以后定时任务推荐使用ScheduledThreadPoolExecutor。java
一、快速入门多线程
Timer运行在后台,能够执行任务一次,或按期执行任务。TimerTask类继承了Runnable接口,所以具有多线程的能力。一个Timer能够调度任意多个TimerTask,全部任务都存储在一个队列中顺序执行,若是须要多个TimerTask并发执行,则须要建立两个多个Timer。并发
一个简单使用Timer的例子以下:ide
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TimerTest { //被执行的任务必须继承TimerTask,而且实现run方法 static class MyTimerTask1 extends TimerTask { public void run() { System.out.println("爆炸!!!"); } } public static void main(String[] args) throws ParseException { Timer timer = new Timer(); //一、设定两秒后执行任务 //timer.scheduleAtFixedRate(new MyTimerTask1(), 2000,1000); //二、设定任务在执行时间执行,本例设定时间13:57:00 SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Date time = dateFormatter.parse("2014/02/11 14:40:00"); timer.schedule(new MyTimerTask1(), time); } }
二、schedule与scheduleAtFixedRate使用方法spa
schedule(TimerTask task, long delay, long period) --指定任务执行延迟时间线程
schedule(TimerTask task, Date time, long period) --指定任务执行时刻code
scheduleAtFixedRate(TimerTask task, long delay, long period)orm
scheduleAtFixedRate(TimerTask task, Date firstTime, long period)htm
三、schedule与scheduleAtFixedRate区别
1) schedule:
① 注重任务执行的平滑度,也就是说任务队列中某个任务执行延迟了某个时间,接下来的其他任务都会延迟相同时间,来最大限度的保证任务与任务之间的时间间隔的完整性;
② 当程序指定开始时刻(Date time)小于当前系统时刻时,会当即执行一次任务,以后的任务开始执行时间以当前时刻为标准,结合时间间隔计算获得;
例:计划任务程序指定从2014/02/11 18:00:00开始每隔3分钟执行一次任务。若是该程序在18:00:00以前运行,则计划任务程序分别会在18:00:00、18:03:00、18:06:00...等时间点执行任务;若是该程序在18:00:00以后运行,如在18:07:00时刻开始运行程序,计划任务程序判断指定开始执行时刻18:00:00小于当前系统时刻,因而当即执行一次任务,接下来任务时间时刻分别为18:10:00、18:13:00、18:16:00...;而当使用scheduleAtFixedRate执行计划任务时,不管计划任务程序在何时运行,全部任务执行的次数都按照原计划,不会由于程序执行时刻的迟早而改变。而当程序运行时刻比计划任务计划首次执行时间晚时,如一样在18:07:00时刻开始执行程序,则计划任务程序会立马计算程序执行时刻晚于指定时刻,会当即执行(18:07:00-18:00:00)/3+1=3次任务(表明18:00:00、18:03:00和18:06:00三个时刻执行的任务),接下来任务执行时刻是18:09:00、18:12:00等。
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TimerRateFix { public static void main(String[] args) throws ParseException { final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Date startDate = dateFormatter.parse("2014/02/11 18:00:00"); Timer timer = new Timer(); timer.schedule(new TimerTask(){ public void run() { System.out.println("执行任务,当前时刻:" + dateFormatter.format(new Date())); } },startDate,3*60*1000); } }
③ 当执行任务的时间间隔t1大于周期间隔t2时,下一次任务执行时间点相对于上一次任务实际执行完成的时间点,每一个任务的执行时间会延后,第n个计划任务的实际执行时间比预计要延后(t1-t2)*n个时间单位。
例:计划任务程序指定从2014/02/11 18:00:00开始每隔5秒执行一次任务,每次任务执行时间为6秒。当程序在18:00:00以前执行时,schedule分别会在18:00:00、18:00:0六、18:00:12...等时间点执行计划任务,每隔时间点间隔6秒。缘由是根据计划,第一个计划任务应会在18:00:00执行,第二个计划任务应会在18:00:05执行,而在18:00:05时间点,第一个任务才执行了5秒,还须要1秒才执行结束,所以第二个任务不能执行,因而等待1秒后在18:00:06时刻执行,以后每一个任务均如此,均比原定执行时刻有延迟,每一个任务时间间隔为6秒。当使用scheduleAtFixedRate执行计划任务时,第一个计划任务在18:00:00时刻执行,第二个会根据计划在18:00:05执行,第三个会在18:00:10执行,每一个任务执行时间间隔为5秒,详细执行状况以下图所示
图1 schedule与scheduleAtFixedRate任务执行区别
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TimerRateTest { public static void main(String[] args) throws ParseException { final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Timer timer = new Timer(); Date time = dateFormatter.parse("2014/02/11 18:00:00"); //假设程序在2014/02/11 18:00:00以前启动 //一、使用scheduleAtFixedRate,每一个计划任务执行时间点严格为18:00:00、18:00:0五、18:00:10...,当任务执行时间大于时间间隔时可能会有并发状况 //二、使用schedule,每一个计划任务执行时间点根据上一个任务执行结束时间及时间间隔来计算 // 当任务执行时间t1>时间间隔t2时,第N个计划任务执行时间点延迟为(t1-t2)*N,执行时间点为18:00:00+t2*(N-1)+(t1-t2)*N // 当任务执行时间t1<=时间间隔t2时,第N个计划任务执行时间点无延迟,执行时间为原计划 timer.scheduleAtFixedRate(new TimerTask(){ public void run() { try { //每一个计划任务执行时间为6秒 Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("结束当前任务,当前时间:"+ dateFormatter.format(new Date())); } },time,5000); //计划任务执行时间间隔为5秒 } }
2) scheduleAtFixedRate:
① 注重任务执行的频度,也就是说计划任务程序开始执行,每隔任务执行的时间点就已经肯定,并不会由于某个任务的延迟而延迟执行其余任务,能够保证任务执行的时间效率;
② 当程序指定开始时刻(Date firstTime)小于当前系统时刻时,会当即执行任务,执行次数为(当前系统时刻-指定开始时刻)/时间间隔,以后的任务开始执行时刻与当前系统时刻无关,仍按照程序指定开始时刻根据时间间隔计算获得;
③ 当执行任务的时间间隔t1大于周期间隔t2时,下一次任务执行时间点仍是按照原定计划不变,所以这种状况,有部分时间断可能有多个任务并发执行;
四、终止Timer线程
1) 调用Timer.cancle()方法。能够在程序任何地方调用,甚至在TimerTask中的run方法中调用;
2) 建立Timer时定义位daemon守护线程(有关守护线程见《Java守护线程》),使用new Timer(true)语句;
3) 设置Timer对象为null,其会自动终止;
4) 调用System.exit方法,整个程序终止。
五、Timer线程的缺点
1) Timer线程不会捕获异常,因此TimerTask抛出的未检查的异常会终止timer线程。若是Timer线程中存在多个计划任务,其中一个计划任务抛出未检查的异常,则会引发整个Timer线程结束,从而致使其余计划任务没法获得继续执行。
2) Timer线程时基于绝对时间(如:2014/02/14 16:06:00),所以计划任务对系统的时间的改变是敏感的。
3) Timer是单线程,若是某个任务很耗时,可能会影响其余计划任务的执行。
所以,JDK1.5以上建议使用ScheduledThreadPoolExecutor来代替Timer执行计划任务。
ScheduledThreadPoolExecutor是JDK1.5之后推出的类,用于实现定时、重复执行的功能,官方文档解释要优于Timer。
一、构造方法
1) ScheduledThreadPoolExecutor(int corePoolSize) 使用给定核心池大小建立一个新定定时线程池
2) ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactorythreadFactory) 使用给定的初始参数建立一个新对象,可提供线程建立工厂
private final static ScheduledThreadPoolExecutor schedual = new ScheduledThreadPoolExecutor(1, new ThreadFactory() { private AtomicInteger atoInteger = new AtomicInteger(0); public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setName("xxx-Thread "+ atoInteger.getAndIncrement()); return t; } });
二、调度方法
1) schedule(Callable callable, long delay, TimeUnit unit); 延迟delay时间后开始执行callable
2) scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit); 延迟initialDelay时间后开始执行command,而且按照period时间周期性重复调用,当任务执行时间大于间隔时间时,以后的任务都会延迟,此时与Timer中的schedule方法相似
3) scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit); 延迟initialDelay时间后开始执行command,而且按照period时间周期性重复调用,这里的间隔时间delay是等上一个任务彻底执行完毕才开始计算,与Timer中scheduleAtFixedRate状况不一样。
图2 ScheduledThreadPoolExecutor.scheduleWithFixedDelay与Timer.scheduleAtFixedRate任务执行区别
三、与Timer相比,优势有:
1) ScheduledThreadPoolExecutor线程会捕获任务重的异常,即便多个计划任务中存在某几个计划任务为捕获异常的状况,也不会影响ScheduledThreadPoolExecutor总线程的工做,不会影响其余计划任务的继续执行。
2) ScheduledThreadPoolExecutor是基于相对时间的,对系统时间的改变不敏感,可是若是执行某一绝对时间(如2014/02/14 17:13:06)执行任务,可能很差执行,此时可以使用Timer。
3) ScheduledThreadPoolExecutor是线程池,如任务数过多或某些任务执行时间较长,可自动分配更多的线程来执行计划任务。
总之,JDK1.5以后,计划任务建议使用ScheduledThreadPoolExecutor。