200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > 智能跳过节假日算法java_Quartz 定时任务使用 —— 排除指定节假日时间执行任务(十一)...

智能跳过节假日算法java_Quartz 定时任务使用 —— 排除指定节假日时间执行任务(十一)...

时间:2021-08-20 08:40:25

相关推荐

智能跳过节假日算法java_Quartz 定时任务使用 —— 排除指定节假日时间执行任务(十一)...

不要混淆了 Quartz 的 Calendar 对象与 Java API 的 java.util.Calendar。它们是应用于不同目的不一样的组件。

Java 的 Calendar 对象是通用的日期和时间工具;许多过去由 Java 的 Date 类提供的功能现在加到了 Calendar 类中了。

Quartz 的 Calendar 专门用于屏闭一个时间区间,使 Trigger 在这个区间中不被触发。

Calendar 排除时间的粒度:

Calendar 接口方法参数的类型是 Long。这说明 Quartz Calendar 能够排除的时间细致毫秒级。你很可能永远都不需要这么细小的位度,因为大部分的 Job 只需要排除特别的日期或许会是小时。然而,假如你真需要排除到毫秒一级的,Calendar 能帮你做到

Quartz 自带的随时可用的 Calendar。

Quartz的BaseCalendar层次结构如下:

参考API:http://www.quartz-/api/2.2.1/index.html

Calendar 名称类用法

BaseCalendarorg.quartz.impl.calendar.BaseCalendar为高级的 Calendar 实现了基本的功能,实现了org.quartz.Calendar接口

AnnualCalendarorg.quartz.impl.calendar.AnnualCalendar排除年中一天或多天

CronCalendarorg.quartz.impl.calendar.CronCalendar日历的这种实现排除了由给定的CronExpression表达的时间集合。 例如,您可以使用此日历使用表达式“* * 0-7,18-23?* *”每天排除所有营业时间(上午8点至下午5点)。

如果CronTrigger具有给定的cron表达式并且与具有相同表达式的CronCalendar相关联,则日历将排除触发器包含的所有时间,并且它们将彼此抵消。

DailyCalendarorg.quartz.impl.calendar.DailyCalendar您可以使用此日历来排除营业时间(上午8点 - 5点)每天。 每个DailyCalendar仅允许指定单个时间范围,并且该时间范围可能不会跨越每日边界(即,您不能指定从上午8点至凌晨5点的时间范围)。 如果属性invertTimeRange为false(默认),则时间范围定义触发器不允许触发的时间范围。 如果invertTimeRange为true,则时间范围被反转 - 也就是排除在定义的时间范围之外的所有时间。

HolidayCalendarorg.quartz.impl.calendar.HolidayCalendar特别的用于从 Trigger 中排除节假日

MonthlyCalendarorg.quartz.impl.calendar.MonthlyCalendar排除月份中的指定数天,例如,可用于排除每月的最后一天

WeeklyCalendarorg.quartz.impl.calendar.WeeklyCalendar排除星期中的任意周几,例如,可用于排除周末,默认周六和周日

注意,所有的Calendar既可以是排除,也可以是包含,取决于:AnnualCalendar:指定每年的哪一天。使用方式如上例。精度是【天】

CronCalendar:指定Cron表达式。精度取决于Cron表达式,也就是最大精度可以【到秒】

DailyCalendar:指定每天的时间段(rangeStartingTime, rangeEndingTime),格式是HH:MM[:SS[:mmm]]。也就是最大精度可以【到毫秒】

HolidayCalendar:指定特定的日期,比如0613。精度到【天】

MonthlyCalendar:指定每月的几号。可选值为1-31。精度是【天】

WeeklyCalendar:指定每星期的星期几,可选值比如为java.util.Calendar.SUNDAY。精度是【天】

使用 Quartz 的 Calendar源码示例

要使用 Quartz Calendar,你只需简单的实例化,并加入你要排除的日期,然后用 Scheduler 注册它。最后把这个 Calendar 实例与你想要使用该Calendar 的每一个 Trigger 实例关联起来

AnnualCalendar

示例:importorg.quartz.*;

importorg.quartz.impl.StdSchedulerFactory;

importorg.quartz.impl.calendar.AnnualCalendar;

importjava.text.SimpleDateFormat;

importjava.util.Calendar;

importjava.util.Date;

importjava.util.GregorianCalendar;

importstaticorg.quartz.DateBuilder.dateOf;

importstaticorg.quartz.JobBuilder.newJob;

importstaticorg.quartz.SimpleScheduleBuilder.simpleSchedule;

importstaticorg.quartz.TriggerBuilder.newTrigger;

/**

*此示例将演示如何使用日历来排除不应该进行调度的时间段。

*/

publicclassCalendarExample{

publicstaticvoidmain(String[]args)throwsException{

SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");

System.out.println("-------初始化----------");

SchedulerFactorysf=newStdSchedulerFactory();

Schedulersched=sf.getScheduler();

//声明一个节假日holidayCalendar,标明要排除的日期

//法定节日是以每年为周期的,所以使用AnnualCalendar而不是HolidayCalendar

AnnualCalendarholidays=newAnnualCalendar();

CalendarfourthOfJuly=newGregorianCalendar(,6,4);//fourthofJuly(July4)七月四日

holidays.setDayExcluded(fourthOfJuly,true);

System.out.println("第一个节假日:"+sdf.format(fourthOfJuly.getTime()));

Calendarhalloween=newGregorianCalendar(,9,31);//halloween(Oct31)万圣节(10月31日)

holidays.setDayExcluded(halloween,true);

System.out.println("第二节假日:"+sdf.format(halloween.getTime()));

Calendarchristmas=newGregorianCalendar(,11,25);//christmas(Dec25)christmas(Dec25)

holidays.setDayExcluded(christmas,true);

System.out.println("第三个节假日:"+sdf.format(christmas.getTime()));

sched.addCalendar("holidays",holidays,false,false);//节假日加入schedule调度器

//开始在万圣节前夜上午10点,开始任务

DaterunDate=dateOf(0,0,10,31,10);

System.out.println("任务开始时间:"+sdf.format(runDate));

JobDetailjob=newJob(SimpleJob.class).withIdentity("job1","group1").build();

SimpleTriggertrigger=newTrigger()

.withIdentity("trigger1","group1")

.startAt(runDate)

.withSchedule(simpleSchedule()

.withIntervalInHours(1).repeatForever())

.modifiedByCalendar("holidays")

.build();

DatefirstRunTime=sched.scheduleJob(job,trigger);

//注意:万圣节(10月31日)是假期,所以直到第二天才会运行!(11月1日)

System.out.println(job.getKey()+"将运行于:"+firstRunTime+"并重复:"+trigger.getRepeatCount()+"次,间隔"+trigger.getRepeatInterval()/1000+"秒");

System.out.println("-------开始Scheduler----------------");

sched.start();

System.out.println("-------等待30秒...--------------");

try{

Thread.sleep(30L*1000L);

}catch(Exceptione){

}

sched.shutdown(true);

System.out.println("-------关闭调度器-----------------");

SchedulerMetaDatametaData=sched.getMetaData();

System.out.println("执行了:"+metaData.getNumberOfJobsExecuted()+"个jobs.");

}

}

CronCalendar

示例importorg.quartz.*;

importorg.quartz.impl.StdSchedulerFactory;

importorg.quartz.impl.calendar.CronCalendar;

importjava.text.SimpleDateFormat;

importjava.util.Date;

importstaticorg.quartz.JobBuilder.newJob;

importstaticorg.quartz.SimpleScheduleBuilder.simpleSchedule;

importstaticorg.quartz.TriggerBuilder.newTrigger;

publicclassSecondExample{

publicstaticvoidmain(String[]args)throwsException{

SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");

System.out.println("-------初始化----------");

SchedulerFactorysf=newStdSchedulerFactory();

Schedulersched=sf.getScheduler();

//允许执行的时间,星期参数:"7"="SAT",2=MON

//【秒】【分钟】【小时】【月中天】【月】【周中天(1-7)】[【年(可省略)】]

StringexcludeExpression;

//这里设置禁用时间段为,每0-20之间,40-59之间不执行

excludeExpression="0-20,40-59****?";

CronCalendarcronCalendar=newCronCalendar(excludeExpression);

//标明要排除的日期每天的17点10分

sched.addCalendar("cronCalendar",cronCalendar,false,false);//节假日加入schedule调度器

DaterunDate=newDate();

System.out.println("任务开始时间:"+sdf.format(runDate));

//任务每10秒执行一次

JobDetailjob=newJob(SimpleJob.class).withIdentity("job1","group1").build();

SimpleTriggertrigger=newTrigger()

.withIdentity("trigger1","group1")

.startAt(runDate)

.withSchedule(simpleSchedule()

.withIntervalInSeconds(10).repeatForever())

.modifiedByCalendar("cronCalendar")

.build();

//触发器加入调度器

DatefirstRunTime=sched.scheduleJob(job,trigger);

System.out.println(job.getKey()+"将运行于:"+sdf.format(firstRunTime)+"并重复:"+trigger.getRepeatCount()+"次,间隔"+trigger.getRepeatInterval()/1000+"秒");

System.out.println("-------开始Scheduler----------------");

sched.start();

try{

System.out.println("-------等待120秒(2分钟)...--------------");

Thread.sleep(120L*1000L);

//dosomething

}catch(Exceptione){

}

sched.shutdown(true);

System.out.println("-------关闭调度器-----------------");

SchedulerMetaDatametaData=sched.getMetaData();

System.out.println("~~~~~~~~~~执行了"+metaData.getNumberOfJobsExecuted()+"个jobs.");

}

}

执行结果-------等待120秒(2分钟)...--------------

任务keygroup1.job1执行时间:-09-1312:10:28

任务keygroup1.job1执行时间:-09-1312:10:38

任务keygroup1.job1执行时间:-09-1312:11:28

任务keygroup1.job1执行时间:-09-1312:11:38

[INFO]13九月12:12:18.158下午main[org.quartz.core.QuartzScheduler]

SchedulerMyScheduler_$_NON_CLUSTEREDshuttingdown.

[INFO]13九月12:12:18.158下午main[org.quartz.core.QuartzScheduler]

SchedulerMyScheduler_$_NON_CLUSTEREDpaused.

[INFO]13九月12:12:18.493下午main[org.quartz.core.QuartzScheduler]

SchedulerMyScheduler_$_NON_CLUSTEREDshutdowncomplete.

-------关闭调度器-----------------

~~~~~~~~~~执行了4个jobs.

DailyCalendar

注意:dailyCalendar.setInvertTimeRange(true); // 时间反转,为true表示只有这次时间段才会被执行,为false表示排除这时间段importorg.quartz.*;

importorg.quartz.impl.StdSchedulerFactory;

importorg.quartz.impl.calendar.DailyCalendar;

importjava.text.SimpleDateFormat;

importjava.util.Date;

importstaticorg.quartz.JobBuilder.newJob;

importstaticorg.quartz.SimpleScheduleBuilder.simpleSchedule;

importstaticorg.quartz.TriggerBuilder.newTrigger;

publicclassDailyCalendarExample{

publicstaticvoidmain(String[]args)throwsException{

SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");

System.out.println("-------初始化----------");

SchedulerFactorysf=newStdSchedulerFactory();

Schedulersched=sf.getScheduler();

DailyCalendardailyCalendar=newDailyCalendar("12:17:30","12:18:20");

dailyCalendar.setInvertTimeRange(true);//时间反转,为true表示只有这次时间段才会被执行,为false表示排除这时间段

//标明要排除的日期每天的17点10分

sched.addCalendar("dailyCalendar",dailyCalendar,false,false);//节假日加入schedule调度器

DaterunDate=newDate();

System.out.println("任务开始时间:"+sdf.format(runDate));

//任务每10秒执行一次

JobDetailjob=newJob(SimpleJob.class).withIdentity("job1","group1").build();

SimpleTriggertrigger=newTrigger()

.withIdentity("trigger1","group1")

.startAt(runDate)

.withSchedule(simpleSchedule()

.withIntervalInSeconds(10).repeatForever())

.modifiedByCalendar("dailyCalendar")

.build();

DatefirstRunTime=sched.scheduleJob(job,trigger);

System.out.println(job.getKey()+"将运行于:"+sdf.format(firstRunTime)+"并重复:"+trigger.getRepeatCount()+"次,间隔"+trigger.getRepeatInterval()/1000+"秒");

System.out.println("-------开始Scheduler----------------");

sched.start();

System.out.println("-------等待360秒(3分钟)...--------------");

try{

Thread.sleep(360L*1000L);

//dosomething

}catch(Exceptione){

}

sched.shutdown(true);

System.out.println("-------关闭调度器-----------------");

SchedulerMetaDatametaData=sched.getMetaData();

System.out.println("~~~~~~~~~~执行了"+metaData.getNumberOfJobsExecuted()+"个jobs.");

}

}

执行结果任务开始时间:-09-1312:16:37

group1.job1将运行于:-09-1312:17:37并重复:-1次,间隔10秒

-------开始Scheduler----------------

[INFO]13九月12:16:37.409下午main[org.quartz.core.QuartzScheduler]

SchedulerMyScheduler_$_NON_CLUSTEREDstarted.

-------等待360秒(3分钟)...--------------

任务keygroup1.job1执行时间:-09-1312:17:37

任务keygroup1.job1执行时间:-09-1312:17:47

任务keygroup1.job1执行时间:-09-1312:17:57

任务keygroup1.job1执行时间:-09-1312:18:07

任务keygroup1.job1执行时间:-09-1312:18:17

[INFO]13九月12:22:37.413下午main[org.quartz.core.QuartzScheduler]

SchedulerMyScheduler_$_NON_CLUSTEREDshuttingdown.

[INFO]13九月12:22:37.413下午main[org.quartz.core.QuartzScheduler]

SchedulerMyScheduler_$_NON_CLUSTEREDpaused.

[INFO]13九月12:22:37.769下午main[org.quartz.core.QuartzScheduler]

SchedulerMyScheduler_$_NON_CLUSTEREDshutdowncomplete.

-------关闭调度器-----------------

~~~~~~~~~~执行了5个jobs.

HolidayCalendar

示例HolidayCalendarholidayCalendar=newHolidayCalendar();

Calendarcalendar=newGregorianCalendar(,10,1);//10月1日

holidayCalendar.addExcludedDate(calendar.getTime());

calendar=newGregorianCalendar(,10,2);//10月2日

holidayCalendar.addExcludedDate(calendar.getTime());

holidayCalendar.getExcludedDates().forEach(date->{

System.out.println("假期日:"+sdf.format(date));

});

sched.addCalendar("holidays",holidayCalendar,false,false);//节假日加入schedule调度器

MonthlyCalendar

月日历,你可以定义一个月当中的若干天,例如你可以设置每个月的第一天触发器不进行触发,当然你还可以定义一个月当中的任何一天。//设置2,3,4月不触发任务

MonthlyCalendarmonthlyCalendar=newMonthlyCalendar();

monthlyCalendar.setDayExcluded(2,true);

monthlyCalendar.setDayExcluded(3,true);

monthlyCalendar.setDayExcluded(4,true);

sched.addCalendar("monthlys",monthlyCalendar,false,false);//节假日加入schedule调度器

WeeklyCalendar

星期日历,可以定义在一个星期当中的星期几几几 是不触发的日期,例如你可以定义么每个周末(星期天)触发器不触发,你也可以定义一周当中的任何一天或是几天。默认情况SATURDAY ,SUNDAY 这两天是没排除的。

下面的例子设置了每个星期四触发器不触发,并且默认情况周六和周天也是不触发的,这个是默认设置。如果需要周六周日也触发,那么把它清掉就可以了(weeklyCalendar.setDayExcluded(Calendar.SATURDAY , false)像这样)。一个需要注意的地方就是传入参数不能直接写数字星期几,因为老外的日子计算的与我们不一样,需要传入(java.util.Calendar)的常量字段,这样才准确。WeeklyCalendarweeklyCalendar=newWeeklyCalendar();

weeklyCalendar.setDayExcluded(Calendar.THURSDAY,true);

sched.addCalendar("weeklys",weeklyCalendar,false,false);//节假日加入schedule调度器

组合日历的使用

上面的例子都是每一个触发器(trigger)关联一个日历的例子,我们在构建触发器的时候通过.modifiedByCalendar("日历的key")关联一个注册到引擎当中的日历,这种情况已经能够满足我们大部分的需求。

但是系统的需求往往是复杂多变的,假设有这样一种情况,需要一个触发器在 每周一到周五,早8点-晚晚5点 每隔1小时执行,那么该如何使用日历呢?

其实我们不用日历,使用一个CronTrigger也是可以搞定的,我们这里只不过是抛砖引玉而已。

那让我们来写一个组合日历使用的例子:DailyCalendardailyCalendar=newDailyCalendar("8:00:00","17:00:00");

dailyCalendar.setInvertTimeRange(false);

WeeklyCalendarweeklyCalendar=newWeeklyCalendar(dailyCalendar);

sched.addCalendar("weeklyCalendar",weeklyCalendar,false,false);

我们写一个时间间隔的日历dailyCalendar,将其作为参数传递给weeklyCalendar就可以了,这样引擎在计算日历日期的时候会先判断dailyCalendar的时间范围,然后再判断weeklyCalendar是时间范围,当条件都满足的是否,触发器才会被触发,我们分析一下源码:@Override

publicbooleanisTimeIncluded(longtimeStamp){

if(excludeAll==true){

returnfalse;

}

//Testthebasecalendarfirst.Onlyifthebasecalendarnotalready

//excludesthetime/date,continueevaluatingthiscalendarinstance.

if(super.isTimeIncluded(timeStamp)==false){returnfalse;}

java.util.Calendarcl=createJavaCalendar(timeStamp);

intwday=cl.get(java.util.Calendar.DAY_OF_WEEK);

return!(isDayExcluded(wday));

}

我们发现它首先调用if (super.isTimeIncluded(timeStamp) == false) { return false; }奥秘就在这里,我们继续看。publicbooleanisTimeIncluded(longtimeStamp){

if(timeStamp<=0){

thrownewIllegalArgumentException(

"timeStampmustbegreater0");

}

if(baseCalendar!=null){

if(baseCalendar.isTimeIncluded(timeStamp)==false){returnfalse;}

}

returntrue;

}

这里先判断了baseCalendar,这个对象就是在构造参数传递进去的dailyCalendar , 也就是它先试用dailyCalendar 进行日期计算,然后自己在计算,这样就完成了日历的组合使用。

最后在补充说明一下往quartz的引擎中注册日历的方法。

addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers)

这个方法有四个参数calName: 日历的名字,在构建触发器时通过modifiedByCalendar("")这里使用。

calendar:日历对象。

replace:当日历已经存在的情况下是否替换,true=替换, false=不替换 如果不替换还出现重复的情况会抛出异常。

updateTriggers:这个参数比较重要,它的意思是当一个已经存在与调度引擎中的触发器,并且已经引用了一个日历,比如:一个(触发器A)关联了一个日历,这个日历过滤每个星期日。现在过了一段时间这个日历更新了(星期六也过滤),那么这个属性是用来指示触发器是否使用新的日历。不然的话(触发器A)仍然使用旧版本的日历,如果在有新添加到引擎中的触发器才会使用新日历。

我们看一下源码:RAMJobStore 还有其它的Store原理也都一样。publicvoidstoreCalendar(Stringname,

Calendarcalendar,booleanreplaceExisting,booleanupdateTriggers)

throwsObjectAlreadyExistsException{

calendar=(Calendar)calendar.clone();

synchronized(lock){

Objectobj=calendarsByName.get(name);

if(obj!=null&&replaceExisting==false){

thrownewObjectAlreadyExistsException(

"Calendarwithname'"+name+"'alreadyexists.");

}elseif(obj!=null){

calendarsByName.remove(name);

}

calendarsByName.put(name,calendar);

if(obj!=null&&updateTriggers){

Iteratortrigs=getTriggerWrappersForCalendar(name).iterator();

while(trigs.hasNext()){

TriggerWrappertw=trigs.next();

OperableTriggertrig=tw.getTrigger();

booleanremoved=timeTriggers.remove(tw);

trig.updateWithNewCalendar(calendar,getMisfireThreshold());

if(removed){

timeTriggers.add(tw);

}

}

}

}

}

参考文章:/daxin/p/3925619.html

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。