线程池:ScheduledThreadPoolExecutor 源码解析

1、继承关系图

 

2、简介

ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类,继承了父类对线程的管理维护功能,通过还可以执行延迟和定时任务

相对于ThreadPoolExecutor 的变化:

(1)使用内部类ScheduledFutureTask封装任务
(2)使用内部类DelayedWorkQueue作为线程池队列
(3)onShutdown方法基于参数配置化去处理shutdown后的任务
(4)提供decorateTask方法作为ScheduledFutureTask的修饰方法,以便使用者进行扩展

关于 ThreadPoolExecutor 部分的详细介绍:线程池:ThreadPoolExecutor 源码解析

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

 

3、成员变量

public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {
    ......
}

关于  ThreadPoolExecutor 具体看:线程池:ThreadPoolExecutor 源码解析

关于  ScheduledExecutorService 具体看:Executor框架底层:Executor、ExecutorService、AbstractExecutorService 和 ScheduledExecutorService

//shutdown后是否继续执行定时任务
private volatile boolean continueExistingPeriodicTasksAfterShutdown;

//shutdown后是否继续执行延迟任务
private volatile boolean executeExistingDelayedTasksAfterShutdown = true;

//cancle方法需要将该任务从队列移除
private volatile boolean removeOnCancel = false;

//任务的序列号
private static final AtomicLong sequencer = new AtomicLong();

 

4、重要内部类

4.1、ScheduledFutureTask

4.1.1、介绍

在ScheduledThreadPoolExecutor中,所有的任务都会被封装成ScheduledFutureTask,无论是runnable还是callable,无论是否需要延迟和定时

private class ScheduledFutureTask<V> extends FutureTask<V> implements RunnableScheduledFuture<V> {
    ......
}

关于  FutureTask 的详细介绍:线程池系列 之 FutureTask 及其底层实现

4.1.2、成员变量

//序列号,CAS自动递增(在构造方法里面)
private final long sequenceNumber;
//周期任务的下一次执行时间
private long time;
//定时周期
private final long period;
//实际任务
RunnableScheduledFuture<V> outerTask = this;
//任务在队列中的下标
int heapIndex;

4.1.3、构造方法

ScheduledFutureTask(Callable<V> callable, long ns) {
    super(callable);
    this.time = ns;
    this.period = 0;
    this.sequenceNumber = sequencer.getAndIncrement();  // 序号递增
} 
ScheduledFutureTask(Runnable r, V result, long ns) {
    super(r, result);
    this.time = ns;
    this.period = 0;
    this.sequenceNumber = sequencer.getAndIncrement();
}
ScheduledFutureTask(Runnable r, V result, long ns, long period) {
    super(r, result);
    this.time = ns;
    this.period = period;
    this.sequenceNumber = sequencer.getAndIncrement();
}

4.1.4、run()

ScheduledFutureTask由任务封装而来,执行任务就是执行ScheduledFutureTask#run()方法,ScheduledThreadPoolExecutor的周期执行的功能也是在run方法中实现

// 重写FutureTask,如果是周期性任务需要重新放入队列
public void run() {
    boolean periodic = isPeriodic();   //任务是否需要周期执行
    if (!canRunInCurrentRunState(periodic))   // 检查当前状态 不能执行任务,则取消任务
        cancel(false);

    else if (!periodic)   //如果不是周期任务,调用FutureTask.run()执行任务(非周期任务直接执行)
        ScheduledFutureTask.super.run();  

    // 到这里说明是:周期性任务
    else if (ScheduledFutureTask.super.runAndReset()) {  //周期任务的执行
    //FutureTask的runAndReset方法
    // 与run方法的不同就是正常完成后任务的状态不会变化,依旧是NEW,且返回值为成功或失败,不会设置result属性
        setNextRunTime();  //设置任务下次执行时间
        reExecutePeriodic(outerTask);    
    }
}

关于 FutureTask.run() 具体见:线程池系列 之 FutureTask 及其底层实现

isPeriodic()

判断任务是否周期执行

public boolean isPeriodic() {
    return period != 0;
}

canRunInCurrentRunState(boolean periodic)   (ScheduledThreadPoolExecutor的成员方法)

检查当前状态能否执行任务;线程池处于RUNNING则必然可以执行,SHUTDOWN状态则需要检查对应的参数设置

boolean canRunInCurrentRunState(boolean periodic) {
    return isRunningOrShutdown(periodic ?
                        continueExistingPeriodicTasksAfterShutdown :   // shutdown后是否继续执行定时任务
                        executeExistingDelayedTasksAfterShutdown);     // shutdown后是否继续执行延迟任务
}

// isRunningOrShutdown是ThreadPoolExecutor中的方法
// 作用是:判断是否是 rujning或者shutdown状态;如果是,返回true
final boolean isRunningOrShutdown(boolean shutdownOK) {
    int rs = runStateOf(ctl.get());
    return rs == RUNNING || (rs == SHUTDOWN && shutdownOK);
}

4.1.5、cancel(boolean mayInterruptIfRunning) 

执行任务的取消逻辑(FutureTask的cancel方法),然后根据removeOnCancel参数设置确定是否需要从队列移除

public boolean cancel(boolean mayInterruptIfRunning) {
    // 调用FutureTask的cancel方法
    boolean cancelled = super.cancel(mayInterruptIfRunning);
    // 如果取消成功,可以移除,并且任务在队列中的下标>=0;则从队列中移除任务
    if (cancelled && removeOnCancel && heapIndex >= 0)
        remove(this);   // ThreadPoolExecutor.remove()
    return cancelled;
}

关于  ThreadPoolExecutor.remove() 见:线程池:ThreadPoolExecutor 源码解析

4.1.6、setNextRunTime()

private void setNextRunTime() {
    long p = period;   //取出周期时间
    if (p > 0)
        time += p;   //time是周期任务的下一次执行时间
    else
        time = triggerTime(-p);
}

// ScheduledThreadPoolExecutor中的方法
long triggerTime(long delay) {
    return now() + ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}

private long overflowFree(long delay) {
    Delayed head = (Delayed) super.getQueue().peek();
    if (head != null) {
        long headDelay = head.getDelay(NANOSECONDS);
        if (headDelay < 0 && (delay - headDelay < 0))
            delay = Long.MAX_VALUE + headDelay;
    }
    return delay;
}

这儿可以看到,周期时间period有正有负,这是ScheduledThreadPoolExecutor的ScheduledAtFixedRate和ScheduledWithFixedDelay的方法区别,前者为正数,后者为负数。
正数时,下一次执行时间为原来的执行时间+周期,即以执行开始时间为基准。
负数时,不考虑溢出情况,下一次执行时间为当前时间+周期,即以执行结束时间为基准。
              如果溢出,下一次执行时间为Long.MAX_VALUE + headDelay。
 

为什么要考虑溢出?

因为在任务加入队列时要将该任务与堆中的部分原有任务在siftUp中通过compareTo进行比较,compareTo比较的是time的差值,如果出现溢出,会导致队列排序不正常,任务的执行混乱的结果。

为什么只考虑向上溢出,不需要考虑向下溢出
因为compareTo中比较的是time的差值,time都是long类型的正数,两个long类型的正数相减显然不会出现向下溢出。

溢出的判断条件是什么?

compareTo中比较的是time的差值,相当于两个节点getDelay的差值。差值最大的必然的该任务与队列头结点任务的差值。因此要判断会不会出现溢出,只需要判断delay - headDelay会不会溢出,delay必然是正数,要出现溢出则headDelay必为负数(也就是任务可以出队列但没有出队列,任务没有及时得到执行)的情况,因此判断条件就是:headDelay < 0 且 (delay - headDelay > Long.MAX_VALUE),即headDelay < 0 && (delay - headDelay < 0)。

为什么delay < (Long.MAX_VALUE >> 1)时不需要考虑溢出?

没想明白

为什么period为正数时不需要考虑溢出?

因为conpareTo比较的是time的差,假设当前任务的time为A,当前队列头结点任务的time为B,因为A比B先出队列,所以A < B。显然A + period - B < period < Long.MAX_VALUE,不会出现溢出的情况。

4.1.7、reExecutePeriodic(RunnableScheduledFuture<?> task)

任务的下一次执行安排

如果当前线程池状态可以执行周期任务,加入队列,并开启新线程。

void reExecutePeriodic(RunnableScheduledFuture<?> task) {
    if (canRunInCurrentRunState(true)) {   // 检查当前状态 能执行周期任务
        // ThreadPoolExecutor.workQueue.add(task)
        super.getQueue().add(task);   //将task添加到线程池workQueue中

        // 再次检查当前状态
        // 如果当前状态不能执行周期任务,将task从队列中移除;如果移除成功,则取消task
        if (!canRunInCurrentRunState(true) && remove(task))
            task.cancel(false);
        // 如果当前状态能执行周期任务
        // 或者不能执行周期任务,但是从队列中移除task失败,则确保至少一个线程在处理任务
        else
            ensurePrestart();   // ThreadPoolExecutor
    }
}

 task.cancel(false) 见:线程池系列 之 FutureTask 及其底层实现

4.1.8、compareTo(Delayed other)

compareTo 作用是加入元素到延迟队列后,内部建立或者调整堆时候会使用该元素的 compareTo 方法与队列里面其他元素进行比较,让最快要过期的元素放到队首。所以无论什么时候向队列里面添加元素,队首的的元素都是最即将过期的元素。如果时间相同,序列号小的排前面

ScheduledFutureTask需要放入DelayedWorkQueue,而DelayedWorkQueue是从小到大进行排序的。
在ScheduledFutureTask,其大小的排序根据执行时间time正序排列,如果相同,在按照序列号sequenceNumber 正序排列。

public int compareTo(Delayed other) {
    // 如果2个指向的同一个对象,则返回0
    if (other == this) 
        return 0;

    // other必须是ScheduledFutureTask类型的
    if (other instanceof ScheduledFutureTask) {
        ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
        long diff = time - x.time;  //当前执行时间(堆顶的task)-x.time

        // 如果堆顶task 比 x更快过期,则返回-1
        if (diff < 0)
            return -1;
        // 如果x 比 堆顶task更快过期,则返回1
        else if (diff > 0)
            return 1;
        // 到这里,说明她两的过期时间相同
        // 比较序列号
        else if (sequenceNumber < x.sequenceNumber)  // 序列号,CAS自动递增
            return -1;
        else
            return 1;
    }

    // 到这里,说明 other不是ScheduledFutureTask类型的
    long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);
    return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}

getDelay(TimeUnit unit)  

用来获取当前任务还有多少时间就过期了

// 元素过期算法,装饰后时间-当前时间,就是即将过期剩余时间
public long getDelay(TimeUnit unit) {
    return unit.convert(time - now(), NANOSECONDS);
}

 

4.2、DelayedWorkQueue

4.2.1、简介

static class DelayedWorkQueue extends AbstractQueue<Runnable> implements BlockingQueue<Runnable> {
    ......
}

关于  BlockingQueue 详细介绍:JDK8系列:刨根问底 之 队列

DelayedWorkQueue是BlockingQueue的实现类,是专门为了ScheduledThreadPoolExecutor设计的队列,队列中的数据结构为最小堆,每个节点的值小于其子节点的值。初始容量为16,节点数量大于数组长度后触发扩容,容量变为原来的1.5倍。 

数据结构:小顶堆,按照到期时间排序,最早过期的task在堆顶

每次删除元素时,将最后一个元素放在堆顶,然后向下遍历,调整顺序(实际代码没有将最后元素放在堆顶这一步)

每次插入元素时,比较插入元素和父节点的大小,向上遍历调整位置

take阻塞操作

        如果 queue==null,则需要等待;

       如果queue!=null,但是堆顶task时间未到,还是要阻塞等待(可能还要调整leader);

       如果queue!=null,并且task时间到了,直接返回堆顶task

4.2.2、成员变量

private static final int INITIAL_CAPACITY = 16;     // 初始容量
// 存放节点
private RunnableScheduledFuture<?>[] queue = new RunnableScheduledFuture<?>[INITIAL_CAPACITY];  
private int size = 0;  // 节点数量

// 控制并发和阻塞等待
private final ReentrantLock lock = new ReentrantLock();
private final Condition available = lock.newCondition();

// 正在阻塞等待头结点的线程
private Thread leader = null;

leader属性用于表示正在阻塞等待头结点的线程,是一种Leader-Follower模式的变种,可以最小化线程的等待时间。同样通过阻塞方式去获取头结点,那么leader线程的等待时间为头结点的延迟时间,其它线程则会陷入阻塞状态(available.await())。leader线程获取到头结点后需要发送信号唤醒其它线程(available.asignAll())。

4.2.3、插入节点

以DelayedWorkQueue#offer(Runnable)为例,其它插入节点的方法都是通过调用该方法完成

public boolean offer(Runnable x) {
    //空值校验
    if (x == null)
        throw new NullPointerException();

    RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x;  //强转类型
    final ReentrantLock lock = this.lock;
    lock.lock();  //加锁
    try {
        int i = size;
        // 超过容量,扩容
        if (i >= queue.length)
            grow();
        size = i + 1;   //更新当前节点数

        //插入的是第一个节点(阻塞队列原本为空)
        if (i == 0) {
            queue[0] = e;
            setIndex(e, 0);  //setIndex(e, 0)用于修改ScheduledFutureTask的heapIndex属性,表示该对象在队列里的下标
        } else {  //阻塞队列非空
            siftUp(i, e);  //在插入新节点后对堆进行调整,进行节点上移,保持其特性(节点的值小于子节点的值)不变
        }

        //插入的是头结点,不需要leader,唤醒等待头结点的阻塞线程
        // 因为leader表示正在阻塞等待头结点的线程,而当前没有线程在等待
        if (queue[0] == e) {
            leader = null;
            available.signal();
        }
    } finally {
        lock.unlock();  //解锁
    }
    return true;
}

(1)setIndex(RunnableScheduledFuture<?> f, int idx)

setIndex(e, 0)用于修改ScheduledFutureTask的heapIndex属性,表示该对象在队列里的下标 

private void setIndex(RunnableScheduledFuture<?> f, int idx) {
    if (f instanceof ScheduledFutureTask)
        ((ScheduledFutureTask)f).heapIndex = idx;
}

(2)siftUp(int k, RunnableScheduledFuture<?> key)  

用于在插入新节点后对堆进行调整,进行节点上移,保持其特性(节点的值小于子节点的值)不变。

从插入的节点开始循环比较,节点小于父节点,则上移。

private void siftUp(int k, RunnableScheduledFuture<?> key) {
    while (k > 0) {
        int parent = (k - 1) >>> 1;  //父节点坐标
        RunnableScheduledFuture<?> e = queue[parent];  //获取父节点的值
        // 如果 节点>= 父节点,确定最终位置
        if (key.compareTo(e) >= 0)
            break;
        // 节点<父节点,将节点向上移动(就是将父节点放在k处)
        queue[k] = e;
        setIndex(e, k);
        k = parent;  //更新父节点
    }
    // 确定key的最后落脚处
    queue[k] = key;
    setIndex(key, k);
}

(3)grow()  扩容

表示扩容,数组长度修改为原来的1.5倍后复制数组中的元素。

private void grow() {
    int oldCapacity = queue.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量为原来的1.5倍
    if (newCapacity < 0) 
        newCapacity = Integer.MAX_VALUE;
    queue = Arrays.copyOf(queue, newCapacity);   //从旧数组 复制到 新数组
}

4.2.4、删除节点(不一定是队列头节点)

DelayedWorkQueue#remove(Runnable)为例,其它删除方法也是通过调用盖方法完成。

public boolean remove(Object x) {
    final ReentrantLock lock = this.lock;
    lock.lock();  //加锁
    try {
        int i = indexOf(x);  //定位x
        //节点元素不存在
        if (i < 0)
            return false;

		//修改元素的heapIndex
        setIndex(queue[i], -1);
        int s = --size;

        //末节点作为替代节点
        RunnableScheduledFuture<?> replacement = queue[s];
        queue[s] = null;  //原本末节点处置空,便于GC
        if (s != i) {
        	//下移,保证该节点的子孙节点保持特性
            siftDown(i, replacement);

            //上移,保证该节点的祖先节点保持特性
            //上移和下移不可能同时发生(!!!)
            // queue[i] == replacement说明下移没有发生
            if (queue[i] == replacement)
                siftUp(i, replacement);
        }
        return true;
    } finally {
        lock.unlock();  //加锁
    }
}

(1)indexOf(Object x)

查找对象在队列中的下标

private int indexOf(Object x) {
    if (x != null) {
        if (x instanceof ScheduledFutureTask) {
            int i = ((ScheduledFutureTask) x).heapIndex;
            // Sanity check; x could conceivably be a
            // ScheduledFutureTask from some other pool.
            if (i >= 0 && i < size && queue[i] == x)
                return i;
        } else {
            for (int i = 0; i < size; i++)
                if (x.equals(queue[i]))
                    return i;
        }
    }
    return -1;
}

(2)siftDown(int k, RunnableScheduledFuture<?> key) 

循环,选择当前节点的子节点中较小的节点与当前节点进行比较,如果当前节点小于子节点,则下移

private void siftDown(int k, RunnableScheduledFuture<?> key) {
    int half = size >>> 1;
    while (k < half) {
        int child = (k << 1) + 1;  //左子节点坐标
        RunnableScheduledFuture<?> c = queue[child];  //c表示左右子节点中的较小者,暂时是左
        int right = child + 1;     //右子节点坐标
        if (right < size && c.compareTo(queue[right]) > 0)
            c = queue[child = right];
        // 当前按节点 <= 子节点中的较小者,找到最终位置
        if (key.compareTo(c) <= 0)
            break;
        // 否则,当前节点下移
        queue[k] = c;
        setIndex(c, k);
        k = child;
    }
    queue[k] = key;
    setIndex(key, k);
}

4.2.5、非阻塞取出头结点poll

DelayedWorkQueue#poll()

// 其实就是调用了 finishPoll
public RunnableScheduledFuture<?> poll() {
   final ReentrantLock lock = this.lock;
   lock.lock();  //加锁
   try {
       RunnableScheduledFuture<?> first = queue[0];
       if (first == null || first.getDelay(NANOSECONDS) > 0)
           return null;
       else
           return finishPoll(first);
   } finally {
       lock.unlock();  //释放锁
   }
}

可以看到
(1)队列中没有元素:返回null。
(2)头结点的延迟时间没到:返回null。(区别于原来的阻塞队列)
(3)头结点达到延迟时间:末节点作为替代节点下移调整堆结构后返回头结点。 

finishPoll(RunnableScheduledFuture<?> f) 

// f是队列头节点(!!!)
private RunnableScheduledFuture<?> finishPoll(RunnableScheduledFuture<?> f) {
    int s = --size;
    RunnableScheduledFuture<?> x = queue[s];  //取出队列尾节点的值(之后放到合适位置)
    queue[s] = null;  //置空,便于GC

    // 尾节点从0开始向下遍历调整顺序
    if (s != 0)
        siftDown(0, x);
    setIndex(f, -1);  //设置f的heapIndex属性
    return f;
}

4.2.6、阻塞获取头结点

以DelayedWorkQueue#take()为例,DelayedWorkQueue#poll(long, TimeUnit)类似,ScheduledThreadPoolExecutor的延迟执行功能就是通过阻塞获取头结点实现的

public RunnableScheduledFuture<?> take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();   //加锁,响应中断
    try {
        // 死循环
        for (;;) {
            RunnableScheduledFuture<?> first = queue[0];  //头节点

            // 如果队列为null,则等待在条件上
            if (first == null)
                available.await();

            // 如果队列非空,还得判断 延时时间是否满足条件
            else {
                long delay = first.getDelay(NANOSECONDS);
                // 如果时间到了,取出头节点
                if (delay <= 0)
                    return finishPoll(first);

                // 头节点时间没到,还不能取出头节点
                first = null; // 等待的时候,不要持有头节点

                //已经存在leader线程,当前线程await阻塞
                if (leader != null)
                    available.await();

                //如果不存在leader线程,当前线程作为leader线程,并制定头结点的延迟时间作为阻塞时间
                else {
                    Thread thisThread = Thread.currentThread();
                    leader = thisThread;
                    try {
                        available.awaitNanos(delay);
                    } finally {
                    	//leader线程阻塞结束
                        if (leader == thisThread)
                            leader = null;
                    }
                }
            }
        }
    } finally {
    //leader线程没有阻塞,可以找到头结点,唤醒阻塞线程
        if (leader == null && queue[0] != null)
            available.signal();
        lock.unlock();
    }
}

 

5、构造方法

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}
public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), handler);
}
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory,
                                       RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                                            new DelayedWorkQueue(), threadFactory, handler);
}

 

6、ScheduledThreadPoolExecutor执行任务

在对DelayedWorkQueue和ScheduledFutureTask这两个内部类进行解析后,正式针对ScheduledThreadPoolExecutor的方法进行解析。

6.1、ScheduledThreadPoolExecutor#schedule

(1)延迟执行callable任务

public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                       long delay,
                                       TimeUnit unit) {
    // 参数异常
    if (callable == null || unit == null)
        throw new NullPointerException();

    // 将当前任务封装成 ScheduledFutureTask
    RunnableScheduledFuture<V> t = decorateTask(callable,
                        new ScheduledFutureTask<V>(callable,triggerTime(delay, unit)));
    // 调用delayedExecute执行任务
    delayedExecute(t);
    return t;
}

(2)延迟执行runnable任务

public ScheduledFuture<?> schedule(Runnable command,
                                   long delay,
                                   TimeUnit unit) {
    // 参数异常
    if (command == null || unit == null)
        throw new NullPointerException();

    // 封装任务 --> RunnableScheduledFuture
    RunnableScheduledFuture<?> t = decorateTask(command,
                               new ScheduledFutureTask<Void>(command, null, triggerTime(delay, unit)));
    delayedExecute(t);  //执行任务
    return t;
}

6.2、 scheduleAtFixedRate

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();

    // 将task --> ScheduledFutureTask(带周期)
    ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command,
                                                                      null,
                                                                    triggerTime(initialDelay, unit),
                                                                      unit.toNanos(period));
    RunnableScheduledFuture<Void> t = decorateTask(command, sft);  //直接返回,没有任何操作,保留给子类扩展
    sft.outerTask = t;   // 实际任务(sft和t一摸一样)
    delayedExecute(t);   //执行任务
    return t;
}

延迟且周期执行,到点执行,开始执行时间 = 上一次的执行开始时间 + period。但是任务执行完成之后,下一次任务才能加入队列。所以准确得说,开始执行时间 = max(上一次执行开始时间 + period, 上一次执行结束时间)

ScheduledFutureTask#decorateTask(Runnable, RunnableFutureTask)

没有做任何操作,直接将task返回。
该方法主要目的是用于子类扩展。

protected <V> RunnableScheduledFuture<V> decorateTask(
    Runnable runnable, RunnableScheduledFuture<V> task) {
    return task;
}

6.3、ScheduledThreadPoolExecutor#scheduleWithFixedDelay

延迟且周期执行,到点执行,固定的延迟时间,开始执行时间 = 上一次任务执行结束时间 + delay。
与scheduleAtFixedRate的区别是通过传入ScheduledFutureTask的period的正负情况确定的,具体实现方式已经在上文ScheduledFutureTask的分析中提到。

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();

    // 封装任务 task --> ScheduledFutureTask
    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;
}

6.4、ScheduledFutureTask#delayedExecute(RunnableScheduledFuture)

校验状态,提交任务,调用ensurePrestart()方法开启线程

private void delayedExecute(RunnableScheduledFuture<?> task) {
	 // 如果线程池关闭了,拒绝任务
     if (isShutdown())
         reject(task);
     // 线程池没有关闭
     else {
         super.getQueue().add(task);   // 任务加入workQueue队列

         //再次检查,线程池状态发生变化,不能够执行了,移出队列,取消任务
         if (isShutdown() &&
                 !canRunInCurrentRunState(task.isPeriodic()) &&
                 remove(task))
             task.cancel(false);
         else
             ensurePrestart();  //确保至少一个线程在处理任务
             // workQueue有task,工作线程非空,它会自动到队列中去任务执行
     }
}
 

 

7、关闭 线程池

ScheduledFutureTask#onShutdown()

线程池状态由RUNNING >> SHUTDOWN变化时的钩子函数。

keepDelayed :shutdown后是否继续执行延迟任务
keepPeriodic :shutdown后是否继续执行周期任务
将不需要执行的延迟任务和周期任务进行取消并从队列中移除。
最后在检查tryTerminate()方法。

void onShutdown() {
    BlockingQueue<Runnable> q = super.getQueue();   //获得workQueue

    // keepDelayed :shutdown后是否继续执行延迟任务
    boolean keepDelayed = getExecuteExistingDelayedTasksAfterShutdownPolicy();
    // keepPeriodic :shutdown后是否继续执行周期任务
    boolean keepPeriodic = getContinueExistingPeriodicTasksAfterShutdownPolicy();

    // 如果shutdown之后,延迟任务 和 周期任务都不需要执行;则遍历workQueue,将全部任务取消,同时清空workQueue
    if (!keepDelayed && !keepPeriodic) {
        for (Object e : q.toArray())
            if (e instanceof RunnableScheduledFuture<?>)
                ((RunnableScheduledFuture<?>) e).cancel(false);
        q.clear();
    }

    // 
    else {
        for (Object e : q.toArray()) {
            if (e instanceof RunnableScheduledFuture) {
                // 封装 task --> RunnableScheduledFuture
                RunnableScheduledFuture<?> t = (RunnableScheduledFuture<?>)e;

                // 满足情况,则取消任务,从workQueue中删除task
                if ((t.isPeriodic() ? !keepPeriodic : !keepDelayed) ||
                                                t.isCancelled()) {
                    if (q.remove(t))
                        t.cancel(false);
                }
            }
        }
    }
    tryTerminate();
}

 

8、提交任务 submit

就是调用的 schedule() 方法

 submit(Callable<T> task)

public <T> Future<T> submit(Callable<T> task) {
    return schedule(task, 0, NANOSECONDS);
}

submit(Runnable task) 

public Future<?> submit(Runnable task) {
    return schedule(task, 0, NANOSECONDS);
}
public <T> Future<T> submit(Runnable task, T result) {
    return schedule(Executors.callable(task, result), 0, NANOSECONDS);
}

 

参考资料:

线程池ScheduledThreadPoolExecutor源码解析

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值