概述
-
ScheduledExecutorService继承于ExecutorService,主要提供任务的延迟和周期性执行的功能。其主要提供了schedule,scheduleAtFixedRate,scheduleWithFixedDelay三个方法,分别用于延迟执行任务,以特定频率周期性执行任务,以特定延迟周期性执行任务。
public interface ScheduledExecutorService extends ExecutorService { // 延迟delay时间执行 public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit); public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit); // 固定频率周期性执行 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit); // 固定延迟周期性执行 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit); }
- schedule:延迟delay时间执行一次;
- scheduleAtFixedRate:以给定的频率周期性执行,分别为initialDelay,initialDelay+period,initialDelay+2period,…,如果出现越界,如initialDelay+period时线程执行时间过长,超过了initialDelay+2period,则下一次从initialDelay+3*period开始执行,不会出现重叠问题,即遵循happen-before原则。如果线程池存在多个线程,则每次执行可能在不同的线程当中。周期性执行的command如果在执行期间抛了异常而没有捕获,则之后不会继续执行该command,即不会再周期性执行了,scheduleWithFixedDelay也是一样;
- scheduleWithFixedDelay:每次执行相隔delay时间,即第一次延迟initialDelay执行,执行完之后等待delay时间后执行第二次,依次类推,跟每次执行的时间无关。
周期性任务停止执行的原因
- scheduleAtFixedRate和scheduleWithFixedDelay在执行周期性任务过程当中,如果任务自身抛了异常而没有捕获,则会导致scheduleAtFixedRate和scheduleWithFixedDelay不会继续周期性执行该任务了,即该任务停止执行。实现原理如下:
-
ScheduledThreadPoolExecutor内部定义了一个内部类ScheduledFutureTask,ScheduledFutureTask继承于FutureTask,由该内部类来对任务进行封装,提供周期性执行的功能。
private class ScheduledFutureTask<V> extends FutureTask<V> implements RunnableScheduledFuture<V> { ... }
-
ScheduledFutureTask的run方法实现如下:主要在runAndReset方法中执行该周期性任务,如果执行成功返回true,则调用setNextRunTime设置下次执行时间;如果该方法返回false,则不再调用setNextRunTime设置下次执行时间,故该周期性任务停止执行。
public void run() { boolean periodic = isPeriodic(); if (!canRunInCurrentRunState(periodic)) cancel(false); else if (!periodic) ScheduledFutureTask.super.run(); // 周期性任务执行,返回true则说明此次执行成功, // 则调用setNextRunTime设置下次执行时间, // 否则不再设置下次执行时间,故不再执行 else if (ScheduledFutureTask.super.runAndReset()) { setNextRunTime(); reExecutePeriodic(outerTask); } }
-
runAndReset的实现:执行任务,在成功执行时,则ran为true,state的值还是NEW,故方法返回值为true。但是如果在执行过程中,即c.call()调用时,出现了异常,则ran设置为false,并在catch块中通过setExeception处理。在setException中将state更新为了EXCEPTIONAL,同时唤醒等待该任务结果的线程,则最终的ran && s == NEW为false,导致runAndReset方法返回false,由上面run的分析可知,该周期任务不再执行。
protected boolean runAndReset() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return false; boolean ran = false; int s = state; try { Callable<V> c = callable; if (c != null && s == NEW) { try { // 执行任务 c.call(); // don't set result ran = true; } catch (Throwable ex) { 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 interrupts // 重置任务状态,如果抛了异常,则 // 在setException中已经将state更新为了EXCEPTIONAL s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } return ran && s == NEW; }
-
setException方法的定义如下:将state更新为EXCEPTIONAL,以便阻塞等待该任务执行结果的线程知道发生了异常,同时调用finishCompletion唤醒这些等待线程。
/** * Causes this future to report an {@link ExecutionException} * with the given throwable as its cause, unless this future has * already been set or has been cancelled. * * <p>This method is invoked internally by the {@link #run} method * upon failure of the computation. * * @param t the cause of failure */ protected void setException(Throwable t) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = t; // 将state更新为EXCEPTIONAL UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state // 唤醒阻塞等待该任务执行结果的线程 finishCompletion(); } }