【转】几种任务调度的 Java 实现方法与比较
简介: 综观目前的 Web 应用,多数应用都具备任务调度的功能。本文由浅入深介绍了几种任务调度的 Java 实现方法,包括 Timer,Scheduler, Quartz 以及 JCron Tab,并对其优缺点进行比较,目的在于给需要开发任务调度的程序员提供有价值的参考。
前言
任务调度是指基于给定时间点,给定时间间隔或者给定执行次数自动执行任务。本文由浅入深介绍四种任务调度的 Java 实现:
TimerScheduledExecutor开源工具包 Quartz开源工具包 JCronTab
此外,为结合实现复杂的任务调度,本文还将介绍 Calendar 的一些使用方法。
Timer
相信大家都已经非常熟悉 java.util.Timer 了,它是最简单的一种实现任务调度的方法,下面给出一个具体的例子:
清单 1. 使用 Timer 进行任务调度
package com.ibm.scheduler; import java.util.Timer; import java.util.TimerTask; public class TimerTest extends TimerTask { private String jobName = ""; public TimerTest(String jobName) { super(); this.jobName = jobName; } @Override public void run() { System.out.println("execute " + jobName); } public static void main(String[] args) { Timer timer = new Timer(); long delay1 = 1 * 1000; long period1 = 1000; // 从现在开始 1 秒钟之后,每隔 1 秒钟执行一次 job1 timer.schedule(new TimerTest("job1"), delay1, period1); long delay2 = 2 * 1000; long period2 = 2000; // 从现在开始 2 秒钟之后,每隔 2 秒钟执行一次 job2 timer.schedule(new TimerTest("job2"), delay2, period2); } } Output: execute job1 execute job1 execute job2 execute job1 execute job1 execute job2
package com.ibm.scheduler;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class ScheduledExecutorTest implements Runnable {private String jobName = "";public ScheduledExecutorTest(String jobName) {super();this.jobName = jobName;}@Overridepublic void run() {System.out.println("execute " + jobName);}public static void main(String[] args) {ScheduledExecutorService service = Executors.newScheduledThreadPool(10);long initialDelay1 = 1;long period1 = 1; // 从现在开始1秒钟之后,每隔1秒钟执行一次job1service.scheduleAtFixedRate( new ScheduledExecutorTest("job1"), initialDelay1,period1, TimeUnit.SECONDS);long initialDelay2 = 1;long delay2 = 1;// 从现在开始2秒钟之后,每隔2秒钟执行一次job2service.scheduleWithFixedDelay( new ScheduledExecutorTest("job2"), initialDelay2,delay2, TimeUnit.SECONDS);}}Output:execute job1execute job1execute job2execute job1execute job1execute job2
package com.ibm.scheduler;import java.util.Calendar;import java.util.Date;import java.util.TimerTask;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class ScheduledExceutorTest2 extends TimerTask {private String jobName = "";public ScheduledExceutorTest2(String jobName) {super();this.jobName = jobName;}@Overridepublic void run() {System.out.println("Date = "+new Date()+", execute " + jobName);}/** * 计算从当前时间currentDate开始,满足条件dayOfWeek, hourOfDay, * minuteOfHour, secondOfMinite的最近时间 * @return */public Calendar getEarliestDate(Calendar currentDate, int dayOfWeek,int hourOfDay, int minuteOfHour, int secondOfMinite) {//计算当前时间的WEEK_OF_YEAR,DAY_OF_WEEK, HOUR_OF_DAY, MINUTE,SECOND等各个字段值int currentWeekOfYear = currentDate.get(Calendar.WEEK_OF_YEAR);int currentDayOfWeek = currentDate.get(Calendar.DAY_OF_WEEK);int currentHour = currentDate.get(Calendar.HOUR_OF_DAY);int currentMinute = currentDate.get(Calendar.MINUTE);int currentSecond = currentDate.get(Calendar.SECOND);//如果输入条件中的dayOfWeek小于当前日期的dayOfWeek,则WEEK_OF_YEAR需要推迟一周boolean weekLater = false;if (dayOfWeek < currentDayOfWeek) {weekLater = true;} else if (dayOfWeek == currentDayOfWeek) {//当输入条件与当前日期的dayOfWeek相等时,如果输入条件中的hourOfDay小于当前日期的//currentHour,则WEEK_OF_YEAR需要推迟一周if (hourOfDay < currentHour) {weekLater = true;} else if (hourOfDay == currentHour) { //当输入条件与当前日期的dayOfWeek, hourOfDay相等时,如果输入条件中的minuteOfHour小于当前日期的//currentMinute,则WEEK_OF_YEAR需要推迟一周if (minuteOfHour < currentMinute) {weekLater = true;} else if (minuteOfHour == currentSecond) { //当输入条件与当前日期的dayOfWeek, hourOfDay, minuteOfHour相等时,如果输入条件中的 //secondOfMinite小于当前日期的currentSecond,则WEEK_OF_YEAR需要推迟一周if (secondOfMinite < currentSecond) {weekLater = true;}}}}if (weekLater) {//设置当前日期中的WEEK_OF_YEAR为当前周推迟一周currentDate.set(Calendar.WEEK_OF_YEAR, currentWeekOfYear + 1);}// 设置当前日期中的DAY_OF_WEEK,HOUR_OF_DAY,MINUTE,SECOND为输入条件中的值。currentDate.set(Calendar.DAY_OF_WEEK, dayOfWeek);currentDate.set(Calendar.HOUR_OF_DAY, hourOfDay);currentDate.set(Calendar.MINUTE, minuteOfHour);currentDate.set(Calendar.SECOND, secondOfMinite);return currentDate;}public static void main(String[] args) throws Exception {ScheduledExceutorTest2 test = new ScheduledExceutorTest2("job1");//获取当前时间Calendar currentDate = Calendar.getInstance();long currentDateLong = currentDate.getTime().getTime();System.out.println("Current Date = " + currentDate.getTime().toString());//计算满足条件的最近一次执行时间Calendar earliestDate = test.getEarliestDate(currentDate, 3, 16, 38, 10);long earliestDateLong = earliestDate.getTime().getTime();System.out.println("Earliest Date = "+ earliestDate.getTime().toString());//计算从当前时间到最近一次执行时间的时间间隔long delay = earliestDateLong - currentDateLong;//计算执行周期为一星期long period = 7 * 24 * 60 * 60 * 1000;ScheduledExecutorService service = Executors.newScheduledThreadPool(10);//从现在开始delay毫秒之后,每隔一星期执行一次job1service.scheduleAtFixedRate(test, delay, period,TimeUnit.MILLISECONDS);}} Output:Current Date = Wed Feb 02 17:32:01 CST 2011Earliest Date = Tue Feb 8 16:38:10 CST 2011Date = Tue Feb 8 16:38:10 CST 2011, execute job1Date = Tue Feb 15 16:38:10 CST 2011, execute job1
YEAR + MONTH + DAY_OF_MONTH YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK YEAR + DAY_OF_YEAR YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
package com.ibm.scheduler;import java.util.Date;import org.quartz.Job;import org.quartz.JobDetail;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import org.quartz.Scheduler;import org.quartz.SchedulerFactory;import org.quartz.Trigger;import org.quartz.helpers.TriggerUtils;public class QuartzTest implements Job {@Override//该方法实现需要执行的任务public void execute(JobExecutionContext arg0) throws JobExecutionException {System.out.println("Generating report - "+ arg0.getJobDetail().getFullName() + ", type ="+ arg0.getJobDetail().getJobDataMap().get("type"));System.out.println(new Date().toString());}public static void main(String[] args) {try {// 创建一个SchedulerSchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();Scheduler sched = schedFact.getScheduler();sched.start();// 创建一个JobDetail,指明name,groupname,以及具体的Job类名,该Job负责定义需要执行任务JobDetail jobDetail = new JobDetail("myJob", "myJobGroup",QuartzTest.class);jobDetail.getJobDataMap().put("type", "FULL"); // 创建一个每周触发的Trigger,指明星期几几点几分执行Trigger trigger = TriggerUtils.makeWeeklyTrigger(3, 16, 38);trigger.setGroup("myTriggerGroup");// 从当前时间的下一秒开始执行trigger.setStartTime(TriggerUtils.getEvenSecondDate(new Date()));// 指明trigger的nametrigger.setName("myTrigger");// 用scheduler将JobDetail与Trigger关联在一起,开始调度任务sched.scheduleJob(jobDetail, trigger);} catch (Exception e) {e.printStackTrace();}}} Output:Generating report - myJobGroup.myJob, type =FULLTue Feb 8 16:38:00 CST 2011Generating report - myJobGroup.myJob, type =FULLTue Feb 15 16:38:00 CST 2011
jobDetail.getJobDataMap().put("myDescription", "my job description"); jobDetail.getJobDataMap().put("myValue", 1998); ArrayList<String> list = new ArrayList<String>(); list.add("item1"); jobDetail.getJobDataMap().put("myArray", list);
public class JobDataMapTest implements Job {@Overridepublic void execute(JobExecutionContext context)throws JobExecutionException {//从context中获取instName,groupName以及dataMapString instName = context.getJobDetail().getName();String groupName = context.getJobDetail().getGroup();JobDataMap dataMap = context.getJobDetail().getJobDataMap();//从dataMap中获取myDescription,myValue以及myArrayString myDescription = dataMap.getString("myDescription");int myValue = dataMap.getInt("myValue");ArrayList<String> myArray = (ArrayListlt;Strin>) dataMap.get("myArray");System.out.println(" Instance =" + instName + ", group = " + groupName + ", description = " + myDescription + ", value =" + myValue + ", array item0 = " + myArray.get(0));}}Output:Instance = myJob, group = myJobGroup, description = my job description, value =1998, array item0 = item1
public SimpleTrigger(String name, String group, Date startTime, Date endTime, int repeatCount, long repeatInterval)
SimpleTrigger trigger= new SimpleTrigger("myTrigger", "myGroup", new Date(), null, 0, 0L);
SimpleTrigger trigger= new SimpleTrigger("myTrigger", "myGroup", new Date(System.currentTimeMillis()+30*1000), null, 0, 60*1000);
Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.YEAR, 2011); calendar.set(Calendar.MONTH, Calendar.JUNE); calendar.set(Calendar.DAY_OF_MONTH, 1); calendar.set(Calendar.HOUR, 8); calendar.set(Calendar.MINUTE, 30); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); Date startTime = calendar.getTime(); Date endTime = new Date (calendar.getTimeInMillis() +24*60*60*1000); SimpleTrigger trigger=new SimpleTrigger("myTrigger", "myGroup", startTime, endTime, 100, 60*60*1000);
Seconds Minutes Hours Day-of-Month Month Day-of-Week Year (Optional field)
0 0 0/3 * * ?
0 3/10 * * * ?
0 0/30 20-23 ? * MON-WED,SAT
0 30 11-14/1 ? * 5L
CronTrigger cronTrigger = new CronTrigger("myTrigger", "myGroup"); try { cronTrigger.setCronExpression("0 0/30 20-13 ? * MON-WED,SAT"); } catch (Exception e) { e.printStackTrace(); }
import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import org.quartz.JobListener;import org.quartz.SchedulerException;public class MyListener implements JobListener{@Overridepublic String getName() {return "My Listener";}@Overridepublic void jobWasExecuted(JobExecutionContext context,JobExecutionException jobException) {if(jobException != null){try {//停止Schedulercontext.getScheduler().shutdown();System.out.println(" Error occurs when executing jobs, shut down the scheduler "); // 给管理员发送邮件…} catch (SchedulerException e) {e.printStackTrace();}}}}
sched.addJobListener(new MyListener()); jobDetail.addJobListener("My Listener"); // listener 的名字
Generating report - myJob.myJob, type =FULL Tue Feb 15 18:57:35 CST 2011 2011-2-15 18:57:35 org.quartz.core.JobRunShell run 信息 : Job myJob.myJob threw a JobExecutionException: org.quartz.JobExecutionException at com.ibm.scheduler.QuartzListenerTest.execute(QuartzListenerTest.java:22) at org.quartz.core.JobRunShell.run(JobRunShell.java:191) at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:516) 2011-2-15 18:57:35 org.quartz.core.QuartzScheduler shutdown 信息 : Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down. Error occurs when executing jobs, shut down the scheduler
SET CURRENT SCHEMA quartz;
drop table qrtz_job_listeners;
# Configure ThreadPool org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 5 org.quartz.threadPool.threadPriority = 4 # Configure Datasources org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.dataSource = db2DS org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.dataSource.db2DS.driver = com.ibm.db2.jcc.DB2Driver org.quartz.dataSource.db2DS.URL = jdbc:db2://localhost:50001/sched org.quartz.dataSource.db2DS.user = quartz org.quartz.dataSource.db2DS.password = passw0rd org.quartz.dataSource.db2DS.maxConnections = 5
package com.ibm.scheduler; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.Trigger; import org.quartz.impl.StdSchedulerFactory; public class QuartzReschedulerTest { public static void main(String[] args) throws SchedulerException { // 初始化一个 Schedule Factory SchedulerFactory schedulerFactory = new StdSchedulerFactory(); // 从 schedule factory 中获取 scheduler Scheduler scheduler = schedulerFactory.getScheduler(); // 从 schedule factory 中获取 trigger Trigger trigger = scheduler.getTrigger("myTrigger", "myTriggerGroup"); // 重新开启调度任务 scheduler.rescheduleJob("myTrigger", "myTriggerGroup", trigger); scheduler.start(); } }
Minutes (0-59) Hours (0-23) Day-of-Month (1-31) Month (1-12/JAN-DEC) Day-of-Week (0-6/SUN-SAT) Command
0 12-15/1 * * * Date
0 1 2 * * mail -s “good” zhjingbj@cn.ibm.com
0/30 20-23 * * MON-WED,SAT echo “normal”
<servlet> <servlet-name>LoadOnStartupServlet</servlet-name> <servlet-class>org.jcrontab.web.loadCrontabServlet</servlet-class> <init-param> <param-name>PROPERTIES_FILE</param-name> <param-value>D:/Scheduler/src/jcrontab.properties</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Mapping of the StartUp Servlet --> <servlet-mapping> <servlet-name>LoadOnStartupServlet</servlet-name> <url-pattern>/Startup</url-pattern> </servlet-mapping>
org.jcrontab.data.file = D:/Scheduler/src/crontab org.jcrontab.data.datasource = org.jcrontab.data.FileSource
*/2 * * * * com.ibm.scheduler.JCronTask1 * * * * * com.ibm.scheduler.JCronTask2#run Hello World
package com.ibm.scheduler;import java.util.Date;public class JCronTask1 {private static int count = 0;public static void main(String[] args) {System.out.println("--------------Task1-----------------");System.out.println("Current Time = " + new Date() + ", Count = "+ count++);}}package com.ibm.scheduler;import java.util.Date;public class JCronTask2 implements Runnable {private static int count = 0;private static String[] args;public JCronTask2(String[] args) {System.out.println("--------------Task2-----------------");System.out.println("Current Time = " + new Date() + ", Count = "+ count++);JCronTask2.args = args;}@Overridepublic void run() {System.out.println("enter into run method");if (args != null && args.length > 0) {for (int i = 0; i < args.length; i++) { System.out.print("This is arg " + i + " " + args[i] + "\n");}}}}
--------------Task2----------------- Current Time = Tue Feb 15 09:22:00 CST 2011, Count = 0 enter into run method This is arg 0 Hello This is arg 1 World --------------Task1----------------- Current Time = Tue Feb 15 09:22:00 CST 2011, Count = 0 --------------Task2----------------- Current Time = Tue Feb 15 09:23:00 CST 2011, Count = 1 enter into run method This is arg 0 Hello This is arg 1 World --------------Task2----------------- Current Time = Tue Feb 15 09:24:00 CST 2011, Count = 2 enter into run method This is arg 0 Hello This is arg 1 World --------------Task1----------------- Current Time = Tue Feb 15 09:24:00 CST 2011, Count = 1
org.jcrontab.sendMail.to= Ther email you want to send to org.jcrontab.sendMail.from=The email you want to send from org.jcrontab.sendMail.smtp.host=smtp server org.jcrontab.sendMail.smtp.user=smtp username org.jcrontab.sendMail.smtp.password=smtp password