ThreadPool--ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor是一种类似Timer的定时器或者说是调度器,和Timer比起来主要有几点好处:

  • 多线程的定时调度,timer是单线程的,每个timer实例只有一个工作线程。
  • 由于继承自ThreadPoolExecutor,更具有灵活性和伸缩性。
  • .没有timer那种线程泄露问题,timer调度的任务如果异常终止,那么整个timer都会被取消,无法执行其他任务。

ScheduledFutureTask是ScheduledThreadPoolExecutor的内部类,也是FutureTask的子类。它的isPeriodic() 决定这个任务是否需要重复执行。

Task重复执行的步骤

1 scheduleWithFixedDelay生成可重复执行的任务

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (delay <= 0)
            throw new IllegalArgumentException();
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(-delay));
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);
        return t;
    }

上面代码生成的ScheduledFutureTask是周期性执行的任务,通过 delayedExecute(t)把它加入到阻塞队列中。并启动线程执行队列中的任务。

到此为止,一个用ScheduledFutureTask包装的任务提交到了线程池的工作队列中,等待线程取出后执行。任务被取出后,会先调用Runnable接口的run方法,那我们来看下run方法做了些什么

public void run() {
       boolean periodic = isPeriodic();
       if (!canRunInCurrentRunState(periodic))// 检查当前线程池状态是否需要取消
            cancel(false);
       else if (!periodic)// 如果不是周期性任务,直接调用父类FutureTask的run方法执行任务。
            ScheduledFutureTask.super.run();
        else if (ScheduledFutureTask.super.runAndReset()) {
//否则,调用父类run方法执行任务,但是不设置结果,以便计算出下次执行的时间后,重复执行。
            setNextRunTime();
            reExecutePeriodic(outerTask);
       }
 }

总结一下:

  1. ScheduledFutureTask表示可调度的异步任务,提交到ScheduledThreadPoolExecutor的任务都会被包装成这个类。
  2. ScheduledThreadPoolExecutor调度任务时会按照延迟时间来,延迟时间最先到期的任务会被首先调度,如果两个任务延迟时间相同,那么还有内部序列号来保证先入先出的顺序。
  3. ScheduledFutureTask被调度后,具体执行时,会判断自己是否是周期性任务。如果不是,任务执行一次;如果时,先执行任务,执行成功后,会算出下次触发事件(延迟时间),然后被再次放入ScheduledThreadPoolExecutor的任务队列中,等待下次被调度执行。

References

  1. 深度解析Java8 – ScheduledThreadPoolExecutor源码解析

results matching ""

    No results matching ""