ScheduledThreadPoolExecutor源码解析

一、概述

ScheduledThreadPoolExecutor继承了ThreadPoolExecutor;内部扩展了任务的ScheduledFutureTask类实现运行任务后修改下一次执行时间;对队列内部采用了再次继承封装的DelayQueue队列,运用了DelayQueue队列的添加排序特性根据最先运行时间进行排序,运用了DelayQueue只有在延迟期满时才能从中提取元素,并且该队列的头部 是延迟期满后保存时间最长的 Delayed 元素。

二、添加一个定时任务过程:

1、形成ScheduledFutureTask对象,该对象实现了Delayed接口的compareTo(Delayed)比较方法和getDelay(TimeUnit unit)获取延迟时间方法。

 2、//如果线程数<核心线程数,创建一个空任务的线程并放入到hashset保存,并启动线程。其实是调用的ThreadPoolExecutor里的addIfUnderCorePoolSize(null)方法

3、添加任务到DelayedWorkQueue队列。上面已经新建线程到线程池并已经启动,线程池里的线程不断轮询是否可以取到任务,如果可以取到任务便执行任务,如果不可以取到便阻塞take方法上。这个轮询取任务过程是用的ThreadPoolExecutor里的过程,和普通的一样。区别在于DelayedWorkQueue队列的take方法,该方法在任务未到期时是无法取到任务的。

4、ScheduledFutureTask内部对run进行再次封装,如果任务是非周期的直接执行;如果是周期任务,执行完成任务后,修改下一次的time   time += 执行周期,并再次添加到队列里;

概括:把任务包装形成ScheduledFutureTask对象,如果目前线程数<核心线程数借助线程池创建空任务线程并激活,把ScheduledFutureTask对象添加到DelayedQueue延迟队列。线程池线程轮询队列,如果任务延迟时间到期进行出队运行任务,否则阻塞等待。

public class ScheduledThreadPoolExecutor

        extends ThreadPoolExecutor  //继承ThreadPoolExecutor  

        implements ScheduledExecutorService {

//  DelayedWorkQueue是一个无界队列,max线程数无用。

  public ScheduledThreadPoolExecutor(int corePoolSize) {

        super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
              new DelayedWorkQueue());

    }

添加定时执行任务的方法如下:

    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (period <= 0)

            throw new IllegalArgumentException();

//形成ScheduledFutureTask对象

        RunnableScheduledFuture<?> t = decorateTask(command,
            new ScheduledFutureTask<Object>(command,
                                            null,
                                            triggerTime(initialDelay, unit),
                                            unit.toNanos(period)));
        delayedExecute(t); //延迟执行任务
        return t;

    }

    private void delayedExecute(Runnable command) {
        if (isShutdown()) {//如果不是running状态,把任务交给拒绝策略
            reject(command);
            return;
        }
        // Prestart a thread if necessary. We cannot prestart it
        // running the task because the task (probably) shouldn't be
        // run yet, so thread will just idle until delay elapses.
        if (getPoolSize() < getCorePoolSize())//如果线程数<核心线程数,创建一个空任务的线程并放入到hashset保存,并启动线程。其实是调用的ThreadPoolExecutor里的addIfUnderCorePoolSize(null)方法
            prestartCoreThread();


        super.getQueue().add(command);//添加任务到DelayedWorkQueue队列。上面已经新建线程到线程池并已经启动,线程池里的线程不断轮询是否可以取到任务,如果可以取到任务便执行任务,如果不可以取到便阻塞take方法上。这个轮询取任务过程是用的ThreadPoolExecutor里的过程,和普通的一样。区别在于DelayedWorkQueue队列的take方法,该方法在任务未到期时是无法取到任务的。

    }

----------------------------------------------------------------------------------------

ScheduledFutureTask内部的结构如下: 

 private final long sequenceNumber;//任务的序列号,当任务的时间相同时,序列号的小先执行。

  private long time;//任务的执行时间

 private final long period;   //任务的执行间隔周期

 /**

         * Overrides FutureTask version so as to reset/requeue if periodic.
         */
        public void run() {
            if (isPeriodic())//是否周期任务,根据period是否=0
                runPeriodic();//执行任务后,修改下一次的time
            else
                ScheduledFutureTask.super.run();//不是周期任务,直接执行任务的方法

        }

        private void runPeriodic() {
            boolean ok = ScheduledFutureTask.super.runAndReset();//执行任务
            boolean down = isShutdown();//是否运行shutdown
            // Reschedule if not cancelled and not shutdown or policy allows
            if (ok && (!down ||
                       (getContinueExistingPeriodicTasksAfterShutdownPolicy() &&
                        !isStopped()))) {
                long p = period;
                if (p > 0)
                    time += p;//根据上一次时间,修改下一次的time。如果间隔为5,本次2、7、12、17,每次间隔固定不变
                else
                    time = triggerTime(-p);//?????根据当前时间+P时间间隔算出下一次时间。如果间隔为5,本次为2下次运行7,由于未获得线程在8运行,下下次运行为8+5=13 
                ScheduledThreadPoolExecutor.super.getQueue().add(this);//再把任务添加到队列
            }
            // This might have been the final executed delayed
            // task.  Wake up threads to check.
            else if (down)
                interruptIdleWorkers();//中断非工作中线程,通过是否获取到线程的运行锁来判断线程是否工作

        }

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)依据固定速率执行,列入间隔为5,严格按照2、7、12执行

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,  TimeUnit unit)依据固定间隔执行,例如间隔为5秒 2开始执行第一次执行后为8秒,第二次执行为8+5=13秒

    public void shutdown() {
        cancelUnwantedTasks();根据策略是否移除队列里任务。
        super.shutdown();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值