任务调度中心
主要依赖quartz.jar相关类 判断cron表达式 , 在下次即将执行的时间在指定时间内时, 从线程池中取线程进行调度 (普通版)
相关类
- MyTrigger.java (主入口)
- MyJob.java
- MyCallable.java
详细说明已都在java代码中体现.
相关jar包
MyTrigger.java (主函数入口)
package com.testdemo.pcis.isc.job.king;
import java.text.SimpleDateFormat;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
/**
* 触发器小例子.
* 1. 模拟从数据库中取完整信息,得到cron表达式
* 2. 判断时间是否在inSeconds(10)秒以内需要执行该任务
* 3. 如果在inSeconds(10)秒以内,从线程池中启动新线程,发送数据
* @author King
* @time 2016/07/07
*/
public class MyTrigger {
//指定任务处于inSeconds秒内时,就开启新线程任务
private static final int inSeconds = 10;//秒
//时间格式化器
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
//线程池
private static final ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
public static void main(String[] args) throws Exception {
// 模拟从数据库中取到一个与计划相关完整单行信息
Object dataFromDb = new Object();
// 模拟dataFromDb该行信息中cron表达式如下
String cronArg = "50 45 14 * * ?";
// 得到计划scheduler
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
// 创建工作任务job
JobDetail job = JobBuilder.newJob().ofType(MyJob.class)
.withIdentity("job1", "group1").build();
// 创建触发器trigger
// 如果cronArg表达式不正确,会报java.text.ParseException,可以用来判断前台页面插入的cron表达式是否合法
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1").startNow()
// .withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(10,2))
.withSchedule(CronScheduleBuilder.cronSchedule(cronArg))
// .withSchedule(CalendarIntervalScheduleBuilder.calendarIntervalSchedule().withIntervalInHours(2))
.build();
// 对该计划指定运行 的工作任务和触发器,该行一定要有,不然触发器的下次执行时间会为null
scheduler.scheduleJob(job, trigger);
// 开始计划的执行,但这里最终目的只需要得到下次执行时间,而不需要真正执行,不然会启动线程增加性能消耗
//s.start();
//判断触发器trigger的下次执行时间是否临近
boolean flag = nearToRun(trigger);
//如果临近,那么开启新线程
if(flag){
startNewThread(trigger,dataFromDb);
}
// 关闭计划scheduler
// s.shutdown(true);
}
/**
* 判断触发器trigger的下次执行时间是否临近inSeconds秒以内(目前为10秒以内)
* @param trigger
* @return
*/
public static boolean nearToRun(Trigger trigger){
// 打印下次执行时间,得到结果
System.out.println("trigger下次触发时间:" + sdf.format(trigger.getNextFireTime()));
// 获取时差 = 该任务下次执行时间 - 当前时间
long delayTimeMillis = trigger.getNextFireTime().getTime() - System.currentTimeMillis();
// 如果 0秒<时差<10秒 ,那么启动线程(这个10秒到时由properties.参数决定)
if (delayTimeMillis > 0 && delayTimeMillis < inSeconds * 1000) {
return true;
}else{
return false;
}
}
/**
* 从线程池开启新线程
* @param trigger 触发器
* @param dataFromDb 从数据库中取到的单行完整信息
* @throws Exception
*/
public static void startNewThread(Trigger trigger,Object dataFromDb) throws Exception{
//模拟从数据库中取到的数据dataInDb
UUID uuid = UUID.randomUUID();
Callable<String> call = new MyCallableImpl(uuid.toString(),dataFromDb);
// 再次获取时差,因为离executor.schedule(arg...)越近,越精确
long delayTimeMillis2 = trigger.getNextFireTime().getTime() - System.currentTimeMillis();
Future future = executor.schedule(call, delayTimeMillis2,TimeUnit.MILLISECONDS);
//如果1 * 3600 * 1000 = 1小时内还得不到返回信息就会报java.util.concurrent.TimeoutException,该参数从dataFromDb中提取
String result = (String) future.get( 1 * 3600 * 1000,TimeUnit.MILLISECONDS);
System.out.println("刚才启动的线程结束了,返回信息为:" + result);
}
}
MyJob.java
为了触发器而创建的类,实际情况并不会运行,但仍旧依赖该job,只要有一个空实现即可.
package com.testdemo.pcis.isc.job.king;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* Job实现类
* @author King
* @time 2016/07/07
*/
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println(sdf.format(new Date())+"I'm runing");
try {
Thread.currentThread().sleep(1*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(sdf.format(new Date())+"I'm runing");
}
}
MyCallable
相当于线程的Runnable,实现类似于run()的call()方法,对真实业务进行调用
package com.testdemo.pcis.isc.job.king;
import java.util.concurrent.Callable;
/**
* Callable实现类
* @author King
* @time 2016/07/07
*/
class MyCallable implements Callable<String> {
private Object dataFromDb;
public String name;
public MyCallable() {
super();
}
public MyCallable(String name, Object dataFromDb) {
this.name = name;
this.dataFromDb = dataFromDb;
}
@Override
public String call() throws Exception {
System.out.println("CallableImpl:" + name + " is start");
// 把dataFromDb数据发送给指定URL,dataFromDb本身包含了URL地址,及超时时间等重要信息
// send(dataFromDb)/自定义一个方法用于发送
// 模拟发送情况,睡一会
Thread.sleep(3 * 1000);
System.out.println("CallableImpl:" + name + "is end");
// 返回相关信息
return "success";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object getDataFromDb() {
return dataFromDb;
}
public void setDataFromDb(Object dataFromDb) {
this.dataFromDb = dataFromDb;
}
}
其它无关tips
得到一些job和trigger的状态
JobDetail jobDetail = scheduler.getJobDetail(new JobKey("job1","group1"));
System.out.println("jobDetail:\t"+jobDetail);
TriggerState triggerState = scheduler.getTriggerState(new TriggerKey("trigger1","group1"));
System.out.println("triggerState:\t"+triggerState);
View Code