diff --git a/doc/docs/schedule.md b/doc/docs/schedule.md index 6ff2bada49dff8d0bc35b7ea3ac7e72e45640ad8..5c09c61a07f0f0a4e396d8d50715731d070b6fac 100644 --- a/doc/docs/schedule.md +++ b/doc/docs/schedule.md @@ -53,6 +53,8 @@ public class MyTask implements Runnable { } } ``` +注意:由于 jdk `ScheduledThreadPoolExecutor` 自身实现的问题,任务的 run 方法如果抛出异常,会造成线程池停止调度, +请务必在任务的 run 方法中使用 try catch 自行捕捉异常。 **方案3:** 使用 JFinal 自带的任务调度方案,参考文档:https://www.jfinal.com/doc/9-1 diff --git a/src/main/java/io/jboot/components/schedule/JbootSafeRunnable.java b/src/main/java/io/jboot/components/schedule/JbootSafeRunnable.java new file mode 100644 index 0000000000000000000000000000000000000000..94270c8831f59ea8733303cc3b55717e90626a55 --- /dev/null +++ b/src/main/java/io/jboot/components/schedule/JbootSafeRunnable.java @@ -0,0 +1,28 @@ +package io.jboot.components.schedule; + +import com.jfinal.log.Log; + +/** + * 使用 try catch 包裹业务代码,防止业务现场抛出异常导致 ScheduledThreadPoolExecutor 终止调度 + * + * @author orangej + * @since 2021-9-26 + */ +public class JbootSafeRunnable implements Runnable { + private static final Log LOG = Log.getLog(JbootSafeRunnable.class); + + private Runnable job; + + public JbootSafeRunnable(Runnable job) { + this.job = job; + } + + @Override + public void run() { + try { + job.run(); + } catch (Throwable ex) { + LOG.error(ex.toString(), ex); + } + } +} diff --git a/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java b/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java index e5cf0a1fe5a2e5e3df6cee2d1e2531af67e6e026..54d67d545c68ed4e96ef0149f315c52e4296d039 100644 --- a/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java +++ b/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java @@ -53,7 +53,7 @@ public class JbootScheduleManager { public JbootScheduleManager() { config = Jboot.config(JbooScheduleConfig.class); - fixedScheduler = new ScheduledThreadPoolExecutor(config.getPoolSize(),new NamedThreadFactory("jboot-scheduler")); + fixedScheduler = new ScheduledThreadPoolExecutor(config.getPoolSize(), new NamedThreadFactory("jboot-scheduler")); File cron4jProperties = new File(PathKit.getRootClassPath(), config.getCron4jFile()); cron4jPlugin = cron4jProperties.exists() @@ -90,6 +90,10 @@ public class JbootScheduleManager { Runnable executeRunnable = runnableClass.getAnnotation(EnableDistributedRunnable.class) == null ? runnable : new JbootDistributedRunnable(runnable, fixedDelayJob.period()); + + // ScheduledThreadPoolExecutor 线程池在遇到未捕获的异常时会终止调度 + // JbootSafeRunnable 可以捕捉业务代码异常,防止线程池意外终止调度 + executeRunnable = new JbootSafeRunnable(executeRunnable); try { scheduleRunnableCache.put(runnableClass, executeRunnable); // modified by lixin 08.08, 用于remove fixedScheduler @@ -106,6 +110,7 @@ public class JbootScheduleManager { Runnable executeRunnable = runnableClass.getAnnotation(EnableDistributedRunnable.class) == null ? runnable : new JbootDistributedRunnable(runnable, fixedRateJob.period()); + executeRunnable = new JbootSafeRunnable(executeRunnable); try { scheduleRunnableCache.put(runnableClass, executeRunnable); // modified by lixin 08.08, 用于 remove fixedScheduler