一个任务调度的业务场景:需要在每个小时后2秒钟从数据库中取得当前小时的数据并进行计算,此时使用Quartz做为任务调度,设置每个小时执行一次,sleep2秒以后执行业务代码。并使用Quartz提供的fireTime作为时间条件获取数据。
先看示例代码:
public class QuartzTest {
public static void main(String[] args) throws SchedulerException {
//String cron="00 00 * * * ?";
//为了测试使用就近时间。
String cron="00 17 * * * ?";
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
Scheduler stdSchedulerFactory=sf.getScheduler();
//50个任务
for(int i=1;i<50;i++){
JobDetail jobDetail=JobBuilder.newJob(Job1.class).build();
Trigger trigger=TriggerBuilder.newTrigger()
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
sched.scheduleJob(jobDetail,trigger);
}
sched.start();
}
}
Job1.class
public class Job1 implements Job{
public void execute(JobExecutionContext context)
throws JobExecutionException {
SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String dateStr=sf.format(context.getFireTime());
//记录任务调度的时间
System.out.println(dateStr);
try {
Thread.sleep(2000);
//do someting with dateStr....
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
得到的结果:
2015-02-01 04:27:00
2015-02-01 04:27:00
2015-02-01 04:27:00
2015-02-01 04:27:00
2015-02-01 04:27:00
2015-02-01 04:27:00
2015-02-01 04:27:00
2015-02-01 04:27:00
2015-02-01 04:27:00
2015-02-01 04:27:00
2015-02-01 04:27:01
2015-02-01 04:27:01
2015-02-01 04:27:01
2015-02-01 04:27:01
2015-02-01 04:27:01
2015-02-01 04:27:01
2015-02-01 04:27:01
2015-02-01 04:27:01
2015-02-01 04:27:01
2015-02-01 04:27:01
2015-02-01 04:27:02
2015-02-01 04:27:02
2015-02-01 04:27:02
2015-02-01 04:27:02
2015-02-01 04:27:02
2015-02-01 04:27:02
2015-02-01 04:27:02
2015-02-01 04:27:02
2015-02-01 04:27:02
2015-02-01 04:27:02
2015-02-01 04:27:03
2015-02-01 04:27:03
2015-02-01 04:27:03
2015-02-01 04:27:03
2015-02-01 04:27:03
2015-02-01 04:27:03
2015-02-01 04:27:03
2015-02-01 04:27:03
2015-02-01 04:27:03
2015-02-01 04:27:03
2015-02-01 04:27:04
2015-02-01 04:27:04
2015-02-01 04:27:04
2015-02-01 04:27:04
2015-02-01 04:27:04
2015-02-01 04:27:04
2015-02-01 04:27:04
2015-02-01 04:27:04
2015-02-01 04:27:04
通过结果发现 拿到的fireTime和预期不一样。
这个时候如果使用fireTime去获取数据就会拿到有误差的数据,也会造成业务结果错误。
通过分析发现 Quartz(v2.1.1) 默认使用的是自带线程池的实现:SimpleThreadPool,并且默认为10个线程,在并发任务达到 10 个以后,再有触发的任务就无法被执行了,只能等待有空闲线程的时候才能得到执,这也是为什么结果中只有前10个结果是符合预期的。
我们可以添加配置文件来修改Quartz的一些默认配置项,并发数:
org.quartz.threadPool.threadCount=50;
也可以直接在代码中修改:
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
Properties pro=new Properties();
pro.setProperty("org.quartz.threadPool.threadCount", "50");
stdSchedulerFactory.initialize(pro);
但是必须指出一点,这个初始线程数并不是越大越好。当并发线程太多时,系统整体性能反而会下降,因为系统把很多时间花在了线程调度上。