main()仅启动Quartz定时线程时如何避免退出?求低负载最优方案
最优解决方案:用CountDownLatch阻塞主线程(零CPU占用)
这问题我太熟了——之前帮好几个朋友解决过类似的Quartz守护进程问题,既要让主线程不退出,又不能搞占资源的无限循环对吧?直接给你两个靠谱方案,优先推荐第一个,保证云服务器负载降到最低:
方案一:CountDownLatch阻塞主线程(最稳妥、零资源浪费)
核心思路
Quartz的调度线程默认是非守护线程,但JVM的规则是:只要还有非守护线程在运行,JVM就不会退出。但问题在于你的main()方法启动Scheduler后会立刻执行完毕,主线程直接退出——如果Quartz的线程被意外配置成守护线程(某些自定义场景),JVM会直接终止所有Quartz线程。
所以最稳妥的方式是让主线程进入无CPU占用的阻塞状态,用CountDownLatch的await()方法,它会让线程进入操作系统级别的等待状态,完全不占用CPU资源,直到计数器被触发(我们这里永远不主动触发,让主线程一直阻塞)。
完整代码示例
import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import java.util.concurrent.CountDownLatch; public class QuartzCronJob { public static void main(String[] args) throws SchedulerException { // 1. 初始化Quartz调度器 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 2. 定义你的定时任务(99%时间闲置,仅触发时执行) JobDetail job = JobBuilder.newJob(MyDailyTask.class) .withIdentity("5MinuteJob", "dailyGroup") .build(); // 3. 配置5分钟间隔的无限触发器 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("5MinuteTrigger", "dailyGroup") .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInMinutes(5) .repeatForever()) .build(); // 4. 关联任务与触发器,启动调度器 scheduler.scheduleJob(job, trigger); scheduler.start(); // 5. 添加优雅关闭钩子(可选,但推荐) Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { scheduler.shutdown(true); // 等待正在执行的任务完成后关闭 System.out.println("Quartz调度器已优雅关闭"); } catch (SchedulerException e) { e.printStackTrace(); } })); // 6. 阻塞主线程,避免JVM退出(零CPU占用) CountDownLatch keepAliveLatch = new CountDownLatch(1); try { keepAliveLatch.await(); } catch (InterruptedException e) { // 捕获中断信号,主动关闭调度器 scheduler.shutdown(); } } // 你的定时任务逻辑实现 public static class MyDailyTask implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 这里写你的任务代码,仅在5分钟间隔触发时执行 System.out.println("定时任务执行:" + new java.util.Date()); } } }
为什么这是最优解?
- 零CPU占用:
CountDownLatch.await()是阻塞式等待,操作系统不会给主线程分配CPU时间片,比while(true)或Thread.sleep()轮询的方案高效太多,完全不会增加服务器负载。 - 不依赖外部配置:不管Quartz线程是守护还是非守护线程,这个方案都能保证程序持续运行,避免配置变更导致的意外退出。
- 支持优雅关闭:通过JVM关闭钩子,在收到终止信号(如Ctrl+C、云服务器停止指令)时,能等待正在执行的任务完成后再退出,避免数据丢失或任务中断。
方案二:修改Quartz线程为非守护线程(简化代码)
如果你的Quartz环境允许修改配置,可以直接将线程池的线程设置为非守护线程,这样即使main()方法执行完毕,JVM会因为Quartz的非守护线程在运行而保持存活。
配置修改
在quartz.properties中添加或修改以下配置:
# 明确设置线程为非守护线程(默认值就是false,但显式配置更保险) org.quartz.threadPool.makeThreadsDaemons=false # 根据你的任务数量调整线程池大小 org.quartz.threadPool.threadCount=3
简化后的main方法
public static void main(String[] args) throws SchedulerException { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 配置Job和Trigger(同方案一) JobDetail job = JobBuilder.newJob(MyDailyTask.class) .withIdentity("5MinuteJob", "dailyGroup") .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("5MinuteTrigger", "dailyGroup") .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInMinutes(5) .repeatForever()) .build(); scheduler.scheduleJob(job, trigger); scheduler.start(); // 不需要任何阻塞代码,main方法结束后JVM不会退出 }
注意事项
这个方案的缺点是依赖外部配置,如果部署时quartz.properties被意外替换或修改,可能导致Quartz线程变成守护线程,进而在main方法退出后被JVM终止。所以方案一更适合生产环境。
内容的提问来源于stack exchange,提问作者triplodrindl
相关产品推荐
相关产品推荐

