5

我有一个 Java 应用程序,它以 SchedulerFactoryBean 的名义使用 Quartz Scheduler。该main()方法获取应用程序上下文,检索根 bean,并开始调度作业。

问题是调度程序在自己的线程中运行,因此当主线程完成提交作业时,它会返回并且调度程序在没有它的情况下继续运行。当调度器最终完成时(或者即使你明确地调用shutdown()它),应用程序就会永远挂在那里。

我有两个解决方案:

  1. 跟踪作业/触发器计数,每当您将作业添加到调度程序时都会增加它。将一个简单的SchedulerListener附加到调度程序,每次调用都会减少此计数triggerFinalized(),并设置while一个内部循环Thread.sleep(),不断检查计数是否达到 0。当它达到时,它将返回到main()方法和应用程序将正常退出。
  2. 从选项 1 中获取自定义 SchedulerListener,并跟踪其中的作业计数。每次调用增加,每次调用jobAdded()减少triggerFinalized()。当计数达到 0 时,调用shutdown()调度程序(或不调用,这实际上并不重要),然后调用System.exit(0).

我已经依次独立地实现了这两个,所以我知道它们实际上都在起作用。问题是它们都很糟糕。一个无限while循环轮询一个值?System.exit(0)? 布莱尔。

有人有更好的方法吗,或者这些真的是我唯一的选择吗?

编辑:在回家的路上思考这个问题时,我得出的结论是,这可能是由于我使用的是 SchedulerFactoryBean 造成的。当 Spring 初始化应用程序上下文时,它会自动启动 - 这似乎将其置于主线程范围之外。如果我使用手动初始化并start()在代码中调用的稍微不同的调度程序,这是否会在主线程中运行调度程序,从而阻塞它直到调度程序完成运行所有作业?或者我还会有这个问题吗?

编辑:儿子... http://quartz-scheduler.org/documentation/quartz-2.x/examples/Example1

为了让程序有机会运行作业,我们然后休眠 90 秒。调度程序在后台运行,应该在这 90 秒内启动作业。

显然,这是行不通的,因为调度程序似乎总是在后台运行。

4

5 回答 5

5

在您SchedulerListener添加一个仅用于同步和锁定的对象。叫它exitLock什么的。您的主线程检索调度程序,设置侦听器,提交所有作业,然后在返回之前执行

Object exitLock = listener.getExitLock();
synchronized (exitLock) {
    exitLock.wait(); // wait unless notified to terminate
}

在每次triggerFinalized()通话时,您的监听器都会递减待处理作业的计数器。一旦所有作业完成执行,您的侦听器就会关闭调度程序。

if (--pendingJobs == 0)
    scheduler.shutdown(); // notice, we don't notify exit from here

一旦调度器关闭,它就会在监听器上调用最后一个回调,我们通知主线程终止,因此程序优雅地退出。

void schedulerShutdown() {
    // scheduler has stopped
    synchronized (exitLock) {
        exitLock.notify(); // notify the main thread to terminate
    }
}

我们没有通知triggerFinalized()所有挂起的作业何时完成的原因是,如果调度程序过早关闭并且不是所有的作业都完成了,我们就会让我们的主线程挂起。通过通知以响应关闭事件,我们确保我们的程序成功退出。

于 2013-05-25T00:24:42.760 回答
2

我认为这里可以是另一种解决方案。

关键点:

  1. 上次执行任务时context.getNextFireTime()返回null
  2. Scheduler.getCurrentlyExecutingJobs == 1表示它是最后执行的作业。

因此,当第 1 点和第 2 点为真时,我们可以关闭调度程序并调用System.exit(0). 这是代码:

听众

public class ShutDownListenet implements JobListener {
    @Override
    public String getName () { return "someName";    }
    @Override
    public void jobToBeExecuted (JobExecutionContext context) {}
    @Override
    public void jobExecutionVetoed (JobExecutionContext context) {}

    @Override
    public void jobWasExecuted (JobExecutionContext context, JobExecutionException jobException) {
        try {
            if (context.getNextFireTime() == null && context.getScheduler().getCurrentlyExecutingJobs().size() == 1) {
                context.getScheduler().shutdown();
                System.exit(0);
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

主函数中的代码 public static void main (String[] args) { Trigger trigger = ... Job job = ...

    JobListener listener = new ShutDownListenet();  
    scheduler.getListenerManager().addJobListener(listener);

    scheduler.scheduleJob(job, trigger);
}

笔记

  1. 我不写synchronized块,但我用 100 个并发作业测试了这段代码,它可以工作。
  2. 未在“复杂”环境中测试:集群或 RMI。(行为可能不同)。

欢迎任何评论。

于 2014-06-11T15:10:18.327 回答
0

如果它帮助别人。我通过添加一个关闭挂钩来解决这个问题,该挂钩触发 Ctrl-C 或脚本中的正常 kill (15)。产生一个新线程并getCurrentlyExecutingJobs().size()每 3 秒轮询一次,并在作业计数器达到零时退出,这意味着所有作业都已完成。

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    try {
        while (jobScheduler.getScheduler().getCurrentlyExecutingJobs().size() > 0) {
            Thread.sleep(3000);
        }
        jobScheduler.getScheduler().clear();
    } catch (Exception e) {
        e.printStackTrace();
    }
}));
于 2017-05-22T11:21:52.447 回答
0

如果您的 Quartz 调度/触发器基于数据库,那么您的程序需要处于活动状态,直到您想要停止它。这可以像下面那样可行。这个想法是挂钩SchedulerListener并在主线程中等待。您需要使用自己的方式来优雅地终止程序,这完全是一个不同的主题。

public static void main(String[] args) {

    AnnotationConfigApplicationContext appContext =  // initialize the your spring app Context

    // register the shutdown hook for JVM
    appContext.registerShutdownHook();

    SchedulerFactoryBean schedulerFactory = appContext.getBean(SchedulerFactoryBean.class);
    scheduler = schedulerFactory.getScheduler();

    final Lock lock = new ReentrantLock();
    final Condition waitCond = lock.newCondition();

    try {
        scheduler.getListenerManager().addSchedulerListener(new SchedulerListener() {

            @Override
            public void jobAdded(JobDetail arg0) {
            }

            @Override
            public void jobDeleted(JobKey arg0) {
            }

            @Override
            public void jobPaused(JobKey arg0) {
            }

            @Override
            public void jobResumed(JobKey arg0) {
            }

            @Override
            public void jobScheduled(Trigger arg0) {
            }

            @Override
            public void jobUnscheduled(TriggerKey arg0) {
            }

            @Override
            public void jobsPaused(String arg0) {
            }

            @Override
            public void jobsResumed(String arg0) {
            }

            @Override
            public void schedulerError(String arg0, SchedulerException arg1) {
            }

            @Override
            public void schedulerInStandbyMode() {
            }

            @Override
            public void schedulerShutdown() {
                lock.lock();
                try {
                    waitCond.signal();
                }
                finally {
                    lock.unlock();
                }
            }

            @Override
            public void schedulerShuttingdown() {
            }

            @Override
            public void schedulerStarted() {
            }

            @Override
            public void schedulerStarting() {
            }

            @Override
            public void schedulingDataCleared() {
            }

            @Override
            public void triggerFinalized(Trigger arg0) {
            }

            @Override
            public void triggerPaused(TriggerKey arg0) {
            }

            @Override
            public void triggerResumed(TriggerKey arg0) {
            }

            @Override
            public void triggersPaused(String arg0) {
            }

            @Override
            public void triggersResumed(String arg0) {
            }

        });

        // start the scheduler. I set the SchedulerFactoryBean.setAutoStartup(false)
        scheduler.start();

        lock.lock();
        try {
            waitCond.await();
        }
        finally {
            lock.unlock();
        }
    } finally {
        scheduler.shutdown(true);
    }
}
于 2017-05-16T19:44:36.647 回答
-2
 while (!scheduler.isShutdown())
 {
     Thread.sleep(2L * 1000L);//Choose reasonable sleep time
 }
于 2015-01-22T03:00:12.140 回答