xiaobenchi

导航

Java并发包中ScheduledThreadPoolExcutor原理探究

Java并发包中ScheduledThreadPoolExcutor原理探究

1. 介绍

ScheduledFutureTask内部还有一个变量period用来表示任务的类型,任务类型如下:

period = 0, 说明当前任务是一次性的,执行完毕后就退出了。

period为负数,说明当前任务为fixed-delay任务,是固定延迟的定时可重复执行任务。

period为正数,说明当前任务为fixed-rate任务,是固定频率的定时可重复执行任务。

ScheduledThreadPoolExecutor的一个构造函数如下,由该构造函数可知线程池队列是DelayedWorkQueue。

//使用改造后的Delayqueue
public ScheduledPoolExecutor(int corePoolSize){
    //调用父类ThreadPoolExcutor的构造函数
    super(corePoolSize,Integer.MAX_VALUE,0,TimeUnit.NANOSECONDS,
         new DelayedWorkedQueue());
}

public ThreadPoolExcutor(int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                        TimeUnit unit,
                        BlockingQueue<Runnable> workQueue){
    this(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,
        Executors.defaultThreadFactory(),defaultHandler);
}

2. 原理剖析

三个重要函数:schedule(Runnable command,long delay,TimeUnit unit)

​ scheduleWithFixedDelay(Runnable command, long initialDelay,long delay,TimeUnit unit)

​ scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)

  • schedule(Runnable command,long delay, TimeUnit unit)方法

    该方法的作用是提交一个延迟执行的任务,任务从提交时间算起延迟单位为unit的delay时间后开始执行。提交的任务不是周期性任务,任务只会执行一次,代码如下。

    public ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit){
        //(1)参数校验
        if(command == null || unit == null)
            throw new NullPointerException();
        //(2)任务转换
        RunnableScheduleFuture<?> t = decorateTask(command,new ScheduledFutureTask<Void> (command,null,triggerTime(delay,unit)));
        //(3)添加任务到延迟队列
        delayedExecute(t);
        return t;
    }
    

    ScheduledFutureTask的构造函数如下。

    ScheduledFutureTask(Runnable r, V result, long ns){
        //调用父类FutureTask的构造函数
        super(r, result);
        this.time = ns;
        this.period = 0; //period为0,说明为一次性任务
        this.sequenceNumber = sequencer.getAndIncrement();
    }
    

    ScheduledFutureTask的run方法

    public void run(){
        //是否只执行一次
        boolean periodic = isPeriodic();
        
        //取消任务
        if(!canRunInCurrentRunState(periodic))
            cancel(false);
        //只执行一次,调用schedule方法的时候
        else if(!periodic)
            ScheduledFutureTask.super.run();
        
        //定时执行
        else if(ScheduledFutureTasks.super.runAndReset()){
            //设置time = time + period
            setNextRunTime();
            
            //重新加入该任务到delay队列
            reExecutePeriodic(outerTask);
        }
    }
    
  • scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)方法

    该方法的作用是,当任务执行完毕后,让其延迟固定时间后再次运行(fixed-delay任务)。其中initialDelay表示提交任务后延迟多少时间开始执行任务command,delay表示当任务执行完毕后延长多少时间后再次运行command任务,unit是initialDelay和delay的时间单位。任务会一直重复运行直到任务运行中抛出了异常,被取消了,或者关闭了线程池。

    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long inititalDelay, long delay,TimeUnit unit){
    	//参数校验
        if(command == null || unit == null)
            throw new NullPointerException();
        if(dealy <= 0)
            throw new IllegalArgumentException();
        
        //任务转换,注意这里是period = -delay < 0
        ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command,null, triggerTime(initialDelay,unit),unit.toNanos(-delay));
        RunnablScheduledFuture<Void> t = decorateTask(command,sft);
        sft.outerTask = t;
        //添加任务到队列
        delayedExecute(t);
        return t;
    }
    
  • scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)方法

    该方法相对起始时间点以固定频率调用指定的任务(fixed-rate任务)。当把任务提交到线程池并延迟initialDelay时间(时间单位为unit)后开始执行任务command。然后从initialDelay+period时间点再次执行,而后在initialDelay+2 * period时间点再次执行,循环往复,直到抛出异常或者调用了任务的cancel方法取消了任务,或者关闭了线程池。

    相对于fixed-delay任务来说,fixed-rate方式执行规则为,时间为initdelday +n*period时启动任务,但是如果当前任务还没有执行完,下一次要执行任务的时间到了,则不会并发执行,下次要执行的任务会延迟执行,要等到当前任务执行完毕后再执行。

3. 总结

本章讲解了ScheduledThreadPoolExecutor的实现原理,如图9-2所示,其内部使用DelayQueue来存放具体任务。任务分为三种,其中一次性执行任务执行完毕就结束了,fixed-delay任务保证同一个任务在多次执行之间间隔固定时间,fixed-rate任务保证按照固定的频率执行。任务类型使用period的值来区分。

posted on 2022-08-23 11:06  小迟在努力  阅读(82)  评论(0)    收藏  举报