JDK1.8源码分析:ScheduledExecutorService和周期性任务停止执行的原因

版权声明:非商业目的可自由转载,转载请标明出处 https://blog.csdn.net/u010013573/article/details/87451176

概述

  • 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);
    
    }
    
  1. schedule:延迟delay时间执行一次;
  2. scheduleAtFixedRate:以给定的频率周期性执行,分别为initialDelay,initialDelay+period,initialDelay+2period,…,如果出现越界,如initialDelay+period时线程执行时间过长,超过了initialDelay+2period,则下一次从initialDelay+3*period开始执行,不会出现重叠问题,即遵循happen-before原则。如果线程池存在多个线程,则每次执行可能在不同的线程当中。周期性执行的command如果在执行期间抛了异常而没有捕获,则之后不会继续执行该command,即不会再周期性执行了,scheduleWithFixedDelay也是一样;
  3. scheduleWithFixedDelay:每次执行相隔delay时间,即第一次延迟initialDelay执行,执行完之后等待delay时间后执行第二次,依次类推,跟每次执行的时间无关。

周期性任务停止执行的原因

  • scheduleAtFixedRate和scheduleWithFixedDelay在执行周期性任务过程当中,如果任务自身抛了异常而没有捕获,则会导致scheduleAtFixedRate和scheduleWithFixedDelay不会继续周期性执行该任务了,即该任务停止执行。实现原理如下:
  1. ScheduledThreadPoolExecutor内部定义了一个内部类ScheduledFutureTask,ScheduledFutureTask继承于FutureTask,由该内部类来对任务进行封装,提供周期性执行的功能。

    private class ScheduledFutureTask<V>
            extends FutureTask<V> implements RunnableScheduledFuture<V> {
            ...
    }
    
  2. 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);
        }
    }
    
  3. 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;
    }
    
  4. 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();
        }
    }
    

猜你喜欢

转载自blog.csdn.net/u010013573/article/details/87451176
今日推荐