两个Quartz-Worker两次执行相同的工作

时间:2011-11-23 13:26:01

标签: java quartz-scheduler scheduling

我们已经实施了石英调度。每个生产的工作都有不同的关键。到目前为止工作正常。昨天我们遇到了一个问题,因为两个不同的Quartz-Worker线程正在执行两次或三次相同的工作(没有特定的行为)。 我们不能使线程池大小为1,因为我们需要并发作业。

关于我们预定工作的一个值得注意的事情是,它在每次运行时重新安排(每天,每周或每月),即如果一项工作计划每天运行,那么它将在接下来的24小时内重新安排,但随机预定义(比如说) 3小时)时间窗口。例如,如果一份工作今天在4:10(即4:00到7:00)之间运行,那么我们的工作将在4:00到7:00之间的某个随机时间自行重新安排到明天。它可能是4:01或6:59或5:23或给定时间窗口中的任何其他值。这个过程也运行良好,大多数情况下仍然可以正常工作,除非在某些情况下我们的重新调度算法未能在接下来的24小时内安排好。相反,它会在接下来的10秒,1小时或任何其他随机值中安排自己。但它最终在2-3次错误的重新安排之后稳定了自己,即它最终在接下来的24小时内安排好了。我们怀疑这可能是由于多个线程访问Calendar对象(我们使用Calendar.getInstance()和cal.add(Calendar.DAY_OF_YEAR,1)在接下来的24小时内重新安排作业)。不知何故,日历实例选择了错误的时间,或者无法在当前时间添加一天。

所以,有两个问题: 1.多个Quartz线程获得相同的工作 2.在某些特定情况下(多线程访问),日历无法添加给定间隔或选择错误的当前时间

任何帮助将不胜感激。尽快回复。 感谢。


感谢您的回复。 我想知道Statefuljob和@DisallowConcurrentExecution注释之间的区别是什么,并将threadPool.threadCount设置为1.

重新安排的代码是......

    Calendar cal = Calendar.getInstance();
    Calendar nextCal = Calendar.getInstance();
    cal.setTimeZone(TimeZone.getTimeZone(obj.getTimeZone()));
    nextCal.setTimeZone(TimeZone.getTimeZone(obj.getTimeZone()));
    Date startTime = null;
    SimpleTrigger trigger = null;

    JobDataMap dataMap = new JobDataMap();
     if (repeatTimeInMillis == null) {
        cal.set(Calendar.HOUR_OF_DAY, obj.getStartTime());
        nextCal.set(Calendar.HOUR_OF_DAY, obj.getStartTime());
        cal.set(Calendar.MINUTE, 0);
        nextCal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        nextCal.set(Calendar.SECOND, 0);
        if (obj.getScheduleType() == ScheduleType.MONTHLY) { // Monthly
    log.info("in monthly schedule");                
            nextCal.add(Calendar.MONTH, 2);
            nextCal.set(Calendar.DAY_OF_MONTH, obj.getDate());
            cal.add(Calendar.MONTH, 1);
            cal.set(Calendar.DAY_OF_MONTH, obj.getDate());
        } else if (obj.getScheduleType() == ScheduleType.WEEKLY) { // Weekly
    log.info("in weekly schedule");                
            nextCal.add(Calendar.WEEK_OF_YEAR, 2);
            nextCal.set(Calendar.DAY_OF_WEEK, obj.getDay());
            cal.add(Calendar.WEEK_OF_YEAR, 1);
            cal.set(Calendar.DAY_OF_WEEK, obj.getDay());
        } else if (obj.getScheduleType() == ScheduleType.DAILY) { // Daily
    log.info("in daily schedule");                
    nextCal.add(Calendar.DAY_OF_YEAR, 2);
            cal.add(Calendar.DAY_OF_YEAR, 1);
        }

        long time = obj.getTimeWindow() * 60 * 60 * 1000;
        time = Math.round(time * Math.random());
        cal.setTimeInMillis(cal.getTimeInMillis() + time);
        startTime = cal.getTime();
        nextCal.setTimeInMillis(nextCal.getTimeInMillis() + time);
        repeatTimeInMillis = nextCal.getTimeInMillis() - cal.getTimeInMillis();

        log.info("Rescheduling job at " + startTime);
        trigger = newTrigger().usingJobData(dataMap)
                .withIdentity(obj.getScheduleJobName(), obj.getScheduleJobGroup()).startAt(startTime)
                .withSchedule(simpleSchedule().withIntervalInMilliseconds(repeatTimeInMillis).repeatForever())
                .build();
    } else {
        log.info("Rescheduling job next " + repeatTimeInMillis + " milliseconds.");
        cal.setTimeInMillis(cal.getTimeInMillis() + repeatTimeInMillis);
        startTime = cal.getTime();
        trigger = newTrigger().usingJobData(dataMap)
                .withIdentity(obj.getScheduleJobName(), obj.getScheduleJobGroup()).startAt(startTime)
                .withSchedule(simpleSchedule().withIntervalInMilliseconds(repeatTimeInMillis).withRepeatCount(1)).build();
    }

1 个答案:

答案 0 :(得分:3)

StatefulJob接口和@DisallowConcurrentExecution注释做同样的事情。

来自DisallowConcurrentExecution javadoc

  

将Job类标记为不能有多个实例的Job类   同时执行....

     

这可以用来代替实现StatefulJob标记   在Quartz 2.0之前使用的接口

将threadPool.threadCount属性设置为1意味着最多可以执行任何的作业

使用这些解决方案中的任何一个都将停止并发执行的作业,并导致任何触发器被放入队列中,以便在上一个触发器实例完成时执行