ScheduledThreadPoolExecutor踩过最痛的坑( 二 )

这个方法主要做了两个事情:

  • 将执行任务和参数包装成ScheduledFutureTask对象
  • 调用delayedExecute方法延迟执行任务
2.延迟或周期性任务的主要执行方法,主要是将任务丢到队列中,后续由工作线程获取执行 。
// ScheduledThreadPoolExecutor#delayedExecuteprivate void delayedExecute(RunnableScheduledFuture<?> task) {if (isShutdown())reject(task);else {// 将任务丢到阻塞队列中super.getQueue().add(task);if (isShutdown() &&!canRunInCurrentRunState(task.isPeriodic()) &&remove(task))task.cancel(false);else// 开启工作线程,去执行任务,或者从队列中获取任务执行ensurePrestart();}}3.现在任务已经在队列中了,我们看下任务执行的内容是什么,还记得前面的包装对象ScheduledFutureTask类,它的实现类是ScheduledFutureTask,继承了Runnable类 。
// ScheduledFutureTask#run方法public void run() {// 是不是周期性任务boolean periodic = isPeriodic();if (!canRunInCurrentRunState(periodic))cancel(false);// 不是周期性任务的话,直接调用一次下面的runelse if (!periodic)ScheduledFutureTask.super.run();// 如果是周期性任务,则调用runAndReset方法,如果返回true,继续执行else if (ScheduledFutureTask.super.runAndReset()) {// 设置下次调度时间setNextRunTime();// 重新执行调度任务reExecutePeriodic(outerTask);}}
  • 这里的关键就是看ScheduledFutureTask.super.runAndReset()方法是否返回true,如果是true的话继续调度 。
4.runAndReset方法也很简单,关键就是看报异常如何处理 。
// FutureTask#runAndResetprotected boolean runAndReset() {if (state != NEW ||!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))return false;// 是否继续下次调度,默认falseboolean ran = false;int s = state;try {Callable<V> c = callable;if (c != null && s == NEW) {try {// 执行任务c.call();// 执行成功的话,设置为trueran = true;// 异常处理,关键点} catch (Throwable ex) {// 不会修改ran的值,最终是false,同时也不打印异常堆栈setException(ex);}}} finally {// runner must be non-null until state is settled to// prevent concurrent calls to run()runner = null;// state must be re-read after nulling runner to prevent// leaked interruptss = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}// 返回结果return ran && s == NEW;}
  • 关键点ran变量,最终返回是不是下次继续调度执行
  • 如果抛出异常的话,可以看到不会修改ran为true 。
总结JAVA的
ScheduledThreadPoolExecutor定时任务线程池所调度的任务中如果抛出了异常,并且异常没有捕获直接抛到框架中,会导致ScheduledThreadPoolExecutor定时任务不调度了 。这个结论希望大家一定要记住,不然非常坑,关键是有时候测试环境、开发环境还无法复现,有一定的随机性,真的到了生产就完蛋了 。
关于这些知识点,我们不仅要知其然,还要知其所以然,这样才会记忆深刻,不然很容易遗忘 。

【ScheduledThreadPoolExecutor踩过最痛的坑】


推荐阅读