手撕源码系列之线程池 -- ScheduledThreadPoolExecutor(一)

简介:ScheduledThreadPoolExecutor用于执行周期性或延时性的定时任务,它继承了ThreadPoolExecutor,在原有基础上实现的任务调度线程池,内部使用延时工作队列DelayedWorkQueue实现对任务的延时调度。

构造方法

简介: ScheduledThreadPoolExecutor类继承的是ThreadPoolExecutor。而且它的构造方法都是super调用父类方法。
由构造方法可以得知,ScheduledThreadPoolExecutor的一些默认参数:

  • 最大线程数是Integer.MAX_VALUE
  • 默认空闲线程存活时间是0纳秒。
  • 默认缓存队列是DelayedWorkQueue(DelayedWorkQueue内部使用一个初始容量为16的数组来保存任务,容量不够时会按照现有容量的1.5倍进行扩容,最大容量可达Integer.MAX_VALUE。)
    可设置的参数仅为核心线程数,拒绝策略和线程工厂
	public ScheduledThreadPoolExecutor(int corePoolSize) {
    
    
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }


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

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


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

重要内部类

ScheduledFutureTask

谱系

在这里插入图片描述

1.源码

1.1 成员变量

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

        /** Sequence number to break ties FIFO 
        * 任务在队列中的序号
        */
        private final long sequenceNumber;

        /** The time the task is enabled to execute in nanoTime units 
        * 任务应该执行的时间,以纳秒计算
        * */
        private long time;

        /**
         * Period in nanoseconds for repeating tasks.  
         * A positive value indicates fixed-rate execution.
         * A negative value indicates fixed-delay execution. 
         * A value of 0 indicates a non-repeating task.
          * 任务重复执行的时间间隔,以纳秒计算
		 * 当其为正值时,表示固定速率执行
		 * 当其为负值时,表示固定延迟执行
		 * 当其为0时,表示是非重复任务
         */
        private final long period;

        /** The actual task to be re-enqueued by reExecutePeriodic */
        RunnableScheduledFuture<V> outerTask = this;

        /**
         * Index into delay queue, to support faster cancellation.
         */
        int heapIndex;

        /**
         * Creates a one-shot action with given nanoTime-based trigger time.
         */
        ScheduledFutureTask(Runnable r, V result, long ns) {
    
    
            super(r, result);
            this.time = ns;
            this.period = 0;
            this.sequenceNumber = sequencer.getAndIncrement();
        }

        /**
         * Creates a periodic action with given nano time and period.
         */
        ScheduledFutureTask(Runnable r, V result, long ns, long period) {
    
    
            super(r, result);
            this.time = ns;
            this.period = period;
            this.sequenceNumber = sequencer.getAndIncrement();
        }

        /**
         * Creates a one-shot action with given nanoTime-based trigger time.
         */
        ScheduledFutureTask(Callable<V> callable, long ns) {
    
    
            super(callable);
            this.time = ns;
            this.period = 0;
            this.sequenceNumber = sequencer.getAndIncrement();
        }

        public long getDelay(TimeUnit unit) {
    
    
            return unit.convert(time - now(), NANOSECONDS);
        }

        public int compareTo(Delayed other) {
    
    
            if (other == this) // compare zero if same object
                return 0;
            if (other instanceof ScheduledFutureTask) {
    
    
                ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
                long diff = time - x.time;
                if (diff < 0)
                    return -1;
                else if (diff > 0)
                    return 1;
                else if (sequenceNumber < x.sequenceNumber)
                    return -1;
                else
                    return 1;
            }
            long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);
            return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
        }

        /**
         * Returns {@code true} if this is a periodic (not a one-shot) action.
         *
         * @return {@code true} if periodic
         */
        public boolean isPeriodic() {
    
    
            return period != 0;
        }

        /**
         * Sets the next time to run for a periodic task.
         */
        private void setNextRunTime() {
    
    
            long p = period;
            if (p > 0)
                time += p;
            else
                time = triggerTime(-p);
        }

        public boolean cancel(boolean mayInterruptIfRunning) {
    
    
            boolean cancelled = super.cancel(mayInterruptIfRunning);
            if (cancelled && removeOnCancel && heapIndex >= 0)
                remove(this);
            return cancelled;
        }

        /**
         * Overrides FutureTask version so as to reset/requeue if periodic.
         */
        public void run() {
    
    
            boolean periodic = isPeriodic();
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            else if (!periodic)
                ScheduledFutureTask.super.run();
            else if (ScheduledFutureTask.super.runAndReset()) {
    
    
                setNextRunTime();
                reExecutePeriodic(outerTask);
            }
        }
    }
  1. sequenceNumber : 用于标识在等待队列中的顺序,因为可能会碰到多个任务的延时是一样的,所以需要另一个序号进行区分标记;
  2. time : 字段用于记录计算出来的任务执行时间;
  3. period:字段用于标识任务是否是定时任务,当该字段为正值时,表示是周期性重复任务,当该值为负值时,表示是延迟性重复任务,当该值为0时,表示是非重复任务;
  4. outerTask:用于记录当前任务,它的默认值就指向自己;该成员变量主要用在重复性任务中,当前一次任务执行结束时,任务会通过outerTask将自己再次加入到等待队列中,以实现重复执行,它的作用会在后面的源码中进行讲解;
  5. heapIndex:用于记录任务在队列中的堆排序顺序。ScheduledThreadPoolExecutor内部使用DelayedWorkQueue作为任务的等待队列,该队列的具体实现其实是一个小顶堆结构,heapIndex则记录了每个任务在堆中的排序顺序

1.2 构造方法

		/**
         * Creates a one-shot action with given nanoTime-based trigger time.
         * 根据给定时间,创建一个执行一次的任务
         */
        ScheduledFutureTask(Runnable r, V result, long ns) {
    
    
            super(r, result);
            this.time = ns;
            this.period = 0;
            this.sequenceNumber = sequencer.getAndIncrement();
        }

        /**
         * Creates a periodic action with given nano time and period.
         * 根据给定开始时间和间隔时间,创建周期性任务
         */
        ScheduledFutureTask(Runnable r, V result, long ns, long period) {
    
    
            super(r, result);
            this.time = ns;
            this.period = period;
            this.sequenceNumber = sequencer.getAndIncrement();
        }

        /**
         * Creates a one-shot action with given nanoTime-based trigger time.
         * 根据给定时间,创建一个执行一次的任务
         */
        ScheduledFutureTask(Callable<V> callable, long ns) {
    
    
            super(callable);
            this.time = ns;
            this.period = 0;
            this.sequenceNumber = sequencer.getAndIncrement();
        }

DelayedWorkQueue等待队列

定义

DelayedWorkQueue是ScheduledThreadPoolExecutor的一个静态内部类

2.1 结构

/**
 * Specialized delay queue. To mesh with TPE declarations, this
 * class must be declared as a BlockingQueue<Runnable> even though
 * it can only hold RunnableScheduledFutures.
 * 延时队列,用于装载被执行的任务,然后根据调度配置进行入队和出队操作
 * 小顶堆模式实现
 */
static class DelayedWorkQueue 
		extends AbstractQueue<Runnable>
        implements BlockingQueue<Runnable> 

DelayedWorkQueue 是实现了 BlockingQueue 的阻塞队列,且泛型指定的是Runnable。

2.2 成员变量

		//初始容量为16
		private static final int INITIAL_CAPACITY = 16;
		
		//装任务的数组 扩容的时候按原有的1.5倍
        private RunnableScheduledFuture<?>[] queue =
            new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
            
		//可重入锁
        private final ReentrantLock lock = new ReentrantLock();
        
		//已有元素个数
        private int size = 0;

		//试图执行队首任务的正处于等待状态的线程对象
        private Thread leader = null;

        //等待条件
        private final Condition available = lock.newCondition();

2.3 DelayedWorkQueue堆结构

2.3.1 任务入队

DelayedWorkQueue中的元素会根据ScheduledFutureTask任务对象执行时间的先后以及其在初始化时获取的sequenceNumber从小到大进行排序,以便实现任务的先后调度。

我们首先关注与任务入队的相关的方法:
put(Runnable e)
add(Runnable e)
offer(Runnable e, long timeout, TimeUnit unit)
offer(Runnable x);
实际前三个方法内部都调用了offer(Runnable x),它们的源码如下:

// 添加任务,内部调用offer()
public void put(Runnable e) {
    
    
    offer(e);
}

// 添加任务,内部调用offer()
public boolean add(Runnable e) {
    
    
    return offer(e);
}

// 添加任务,内部调用offer()
public boolean offer(Runnable e, long timeout, TimeUnit unit) {
    
    
    return offer(e);
}

所以主要关注的点在于

// 添加任务
public boolean offer(Runnable x) {
    
    
    // 检查任务
    if (x == null)
        throw new NullPointerException();
    //将任务转换为RunnableScheduledFuture对象
    RunnableScheduledFuture e = (RunnableScheduledFuture)x;
    // 加锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
    
    
        int i = size;
        if (i >= queue.length)
            // 如果任务个数大于等于队列数组容量,进行扩容操作
            grow();
        // size加1
        size = i + 1;
        if (i == 0) {
    
    
            /**
             * i为0,即原始size为0,没有任务
             * 将e放在第0位置
             */
            queue[0] = e;
            // 设置e的heapIndex为0
            setIndex(e, 0);
        } else {
    
    
            // 否则调用siftUp()对e进行添加及上浮操作
            siftUp(i, e);
        }
        /**
         * 如果第0个位置的任务是e,
         * 表示此时入队的是队首元素,可以将leader清空了
         * 同时可以通知其他线程可以竞争成为leader了
         */
        if (queue[0] == e) {
    
    
            // 将leader置为null
            leader = null;
            // 唤醒等待在available上的线程
            available.signal();
        }
    } finally {
    
    
        // 解锁
        lock.unlock();
    }
    return true;
}
2.3.2 任务出队

DelayedWorkQueue的任务出队操作则相对比较麻烦,它提供了
poll()
poll(long timeout, TimeUnit unit)
take()
三个出队方法。其中poll()方法最简单,它是无阻塞模式的出队操作,如果能获取到符合要求的任务就进行出队,否则直接返回null;

扫描二维码关注公众号,回复: 13120037 查看本文章
2.3.3. 任务移除

DelayedWorkQueue提供了remove(Object x)方法可以将队列中的任务进行移除;
remove方法无新颖套路,详情见源码;

猜你喜欢

转载自blog.csdn.net/pontuss/article/details/114117867