ScheduledThreadPoolExecutor源码主要部分解析

ScheduledThreadPoolExecutor继承与基础线程池类ThreadPoolExecutor并实现ScheduledExecutorService接口。

其中ScheduledExecutorService继承与ExecutorService接口并添加了scheduleAtFixedRate和scheduleWithFixedDelay等方法。

两个方法的区别是前者是周期性的按照一定的时间进行任务的执行。如果一个任务执行超过了周期时间,则任务执行完之后会马上进行下一次任务的执行。而后者在这样的情况出现的时候会在任务执行完之后仍然间隔周期时间进行下一次任务的执行。

 

ScheduledThreadPoolExecutor内部封装了一个ScheduledFutureTask<V>类。该类继承与FutureTask<V>实现RunnableScheduledFuture<V>

ScheduledFutureTask<V>内部有对应的任务的比较器。包含sequenceNumber记录任务进入队列的序号。内部的关键方法为runPeriodic()方法

 

private void runPeriodic() {  

  •             boolean ok = ScheduledFutureTask.super.runAndReset();  
  •             boolean down = isShutdown();  
  •             // Reschedule if not cancelled and not shutdown or policy allows  
  •             if (ok && (!down ||  
  •                        (getContinueExistingPeriodicTasksAfterShutdownPolicy() &&  
  •                         !isStopped()))) {  
  •                 long p = period;  
  •                 if (p > 0)  
  •                     time += p;  
  •                 else  
  •                     time = now() - p;  
  •                 ScheduledThreadPoolExecutor.super.getQueue().add(this);  
  •             }  
  •             // This might have been the final executed delayed  
  •             // task.  Wake up threads to check.  
  •             else if (down)  
  •                 interruptIdleWorkers();  
  •         }  
  •   

该方法处理周期性执行的和非周期性执行的任务。其中period传入的值有正有负。这也是scheduleAtFixedRate和scheduleWithFixedDelay对应的区别。最后将相应的任务加入到对应的线程池的阻塞队列中。如果线程池关闭了需要调用对应的线程池关闭的处理。

对应的run方法为:

public void run() {  

  •             if (isPeriodic())  
  •                 runPeriodic();  
  •             else  
  •                 ScheduledFutureTask.super.run();  
  •         }  

 private void delayedExecute(Runnable command) {
        if (isShutdown()) {
            reject(command);
            return;
        }
        if (getPoolSize() < getCorePoolSize())
            prestartCoreThread();

        super.getQueue().add(command);
    }
首次将任务加入到队列中的方法。

 public boolean prestartCoreThread() {
        return addIfUnderCorePoolSize(null);
    }


    private boolean addIfUnderCorePoolSize(Runnable firstTask) {
        Thread t = null;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (poolSize < corePoolSize && runState == RUNNING)                

               t = addThread(firstTask);        

            } finally {            

                        mainLock.unlock();        

                 }        

               if (t == null)            

                        return false;         t.start();         return true; }

如果当前线程数小于核心线程数。则会新创建一个线程。新创建线程的时候会传入首个任务。

ScheduledThreadPoolExecutor中的默认拒绝策略是AbortPolicy 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常 其它的拒绝策略为:

AbortPolicy         -- 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常。
CallerRunsPolicy    -- 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。
DiscardOldestPolicy -- 当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
DiscardPolicy       -- 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。

 FutureTask<V>类的内部是通过Sync和Callable<V>来构建相应的任务Sync是对应的同步器继承于AQS(内部基于一个链表的形式来维护一个等待队列,用其内部的Node节点进行构建)。AQS内部的原理感兴趣的的可以看下,此外ReenTrantLock内部的实现则是基于sync和AQS。

此外ScheduledThreadPoolExecutor内部封装了一个DelayedWorkQueue。其内部应用DelayQueue实现。对DelayQueue感兴趣的可以看下。

 

 

 

posted @ 2017-08-21 18:33  菜鸟麦迪粉  阅读(839)  评论(0编辑  收藏  举报