我有一个Java应用程序,它以SchedulerFactoryBean为幌子使用Quartz Scheduler。 main()
方法获取应用程序上下文,检索根bean,并开始调度作业。
问题是Scheduler在自己的线程中运行,所以当主线程完成提交作业时,它会返回并且Scheduler在没有它的情况下继续运行。当调度程序最终完成时(或者即使你明确地调用它上面的shutdown()
),应用程序也会永远挂在那里。
我有两个解决方案:
triggerFinalized()
时递减此计数,并在其中设置while
循环,其中Thread.sleep()
不断检查以查看是否计数达到0.如果是,它将返回main()
方法,应用程序将正常退出。jobAdded()
,每次呼叫递减triggerFinalized()
。当计数达到0时,在调度程序上调用shutdown()
(或不调用,实际上并不重要),然后调用System.exit(0)
。我已经依次独立实现了这两个,所以我知道它们实际上都有功能。问题是它们都很糟糕。轮询值的无限while
循环? System.exit(0)
? Bleargh。
有人有更好的方法,还是这些是我唯一的选择?
编辑:在回家的路上考虑这个问题时,我得出的结论是,这可能是由于我使用的是SchedulerFactoryBean。当Spring初始化应用程序上下文时,这会自动启动 - 这似乎超出了主线程的范围。如果我使用稍微不同的Scheduler,我手动初始化并在代码中调用start()
,那么它是否会在主线程中运行Scheduler,从而阻止它直到Scheduler完成所有作业的运行?或者我还有这个问题吗?
编辑:...... http://quartz-scheduler.org/documentation/quartz-2.x/examples/Example1
的儿子为了让程序有机会运行该作业,我们再睡眠90秒。调度程序在后台运行,并应在90秒内启动作业。
显然,这不起作用,因为调度程序似乎总是在后台运行。
答案 0 :(得分: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()
中没有通知的原因是,如果调度程序过早关闭而且并非所有作业都已完成,我们就会将主线程挂起。通过响应关闭事件通知我们确保我们的程序成功退出。
答案 1 :(得分:2)
我认为这可以是另一种解决方案。
关键点:
context.getNextFireTime()
返回null
。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){ 触发器触发器= ... 工作= ...
JobListener listener = new ShutDownListenet();
scheduler.getListenerManager().addJobListener(listener);
scheduler.scheduleJob(job, trigger);
}
注意强>
synchronized
块,但是我用100个concurent工作测试了这段代码,它有效。任何评论都很好。
答案 2 :(得分: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);
}
}
答案 3 :(得分:0)
如果它可以帮助别人。我通过添加一个在Ctrl-C上触发的shutdown-hook或从脚本中正常kill(15)来解决这个问题。生成一个新线程并每隔3秒轮询一次getCurrentlyExecutingJobs().size()
,当作业计数器达到零时退出,表示所有作业都已完成。
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
while (jobScheduler.getScheduler().getCurrentlyExecutingJobs().size() > 0) {
Thread.sleep(3000);
}
jobScheduler.getScheduler().clear();
} catch (Exception e) {
e.printStackTrace();
}
}));
答案 4 :(得分:-2)
while (!scheduler.isShutdown())
{
Thread.sleep(2L * 1000L);//Choose reasonable sleep time
}