ScheduledThreadPoolExecutor 源码解析

概述

ScheduledThreadPoolExecutor 常常用来作为延迟任务,或者是周期性执行某个任务
延迟任务简单的使用示如下,如果需要周期性执行任务,使用 scheduleAtFixedRate()

	public static void main(String[] args) throws ClassNotFoundException, ExecutionException, InterruptedException {
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);
        scheduledThreadPoolExecutor.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello world");
            }
        },3,TimeUnit.SECONDS);
        scheduledThreadPoolExecutor.shutdown();
    }

构造方法

首先来关注构造方法

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

其实ScheduledThreadPoolExecutor的本质依然是一个线程池,但是于其他的线程池不同的是使用了 DelayedWorkQueue()

schedule()

public ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        RunnableScheduledFuture<?> t = decorateTask(command,
            new ScheduledFutureTask<Void>(command, null,triggerTime(delay, unit)));
        delayedExecute(t);
        return t;
    }

在schedule 中将 command(具体任务)delay 时间,unit(时间单位)封装成一个 ScheduledFutureTask
然后执行 delayedExecute(t)

	private void delayedExecute(RunnableScheduledFuture<?> task) {
	//判断线程池是否关闭
        if (isShutdown())
            reject(task);//拒绝策略
        else {
        //将这个任务添加到任务队列
            super.getQueue().add(task);
          //判断线程池是否关闭,这部分不考虑
            if (isShutdown() &&
                !canRunInCurrentRunState(task.isPeriodic()) &&
                remove(task))
                task.cancel(false);
            else
                ensurePrestart();
        }
    }

进入ensurePrestart();

	void ensurePrestart() {
        int wc = workerCountOf(ctl.get());
        if (wc < corePoolSize)
            addWorker(null, true);
        else if (wc == 0)
            addWorker(null, false);
    }

线程数小于 corePoolSize ,在addWorker就会new 一个Worker (也就是新建一个线程),反之不会创建
最后在线程池获取任务的时候就会根据DelayedWorkQueue 进行 获取任务
其实这已经是ThreadPoolExecutor的内容了,感兴趣的朋友可以阅读笔者另外一篇博客,连下如下
https://blog.csdn.net/kznsbs/article/details/111958345

DelayedWorkQueue()

说到DelayedWorkQueue 相信大家很容易就想到了DelayQueue,其实DelayedWorkQueue的原理跟DelayQueue也是类似的
简单的看一下这个DelayedWorkQueue 的take()和 put()

put()

		public void put(Runnable e) {
            offer(e);
        }
        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 表示存储的元素个数
                size = i + 1;
                if (i == 0) {//如果是第一个直接赋值到下标0的位置
                    queue[0] = e;
                    setIndex(e, 0);
                } else {//说明数组里有其他的元素,通过getDelay()的大小进行比较,将执行时间短的任务排在最前面
                    siftUp(i, e);
                }
                if (queue[0] == e) {
                    leader = null;
                    available.signal();
                }
            } finally {
                lock.unlock();
            }
            return true;
        }
        

take()

	public RunnableScheduledFuture<?> take() throws InterruptedException {
            final ReentrantLock lock = this.lock;
            //上锁
            lock.lockInterruptibly();
            try {
                for (;;) {
                //前面我们看到队列里面的添加的都是RunnableScheduledFuture的子类ScheduledFutureTask
                    RunnableScheduledFuture<?> first = queue[0];//取第一个元素,因为在put的时候已经排好序了,所以直接取第一个就好了
                    if (first == null)//如果一个元素都没有就进行阻塞
                        available.await();
                    else {
                    	//获取离执行还差多长时间
                        long delay = first.getDelay(NANOSECONDS);
                        if (delay <= 0)//到了执行时间
                        	//就会将这个任务(ScheduledFutureTask)返回出去,但是这里买呢还会进行堆的调整,因为在take的时候永远只会取第一个元素
                            return finishPoll(first);
                        first = null; // don't retain ref while waiting
						
						//如果没到时间进行阻塞
                        if (leader != null)
                            available.await();
                        else {
                            Thread thisThread = Thread.currentThread();
                            leader = thisThread;
                            try {
                                available.awaitNanos(delay);
                            } finally {
                                if (leader == thisThread)
                                    leader = null;
                            }
                        }
                    }
                }
            } finally {
                if (leader == null && queue[0] != null)
                    available.signal();
                lock.unlock();
            }
        }

周期执行和非周期执行

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

周期执行和非周期执行最关键的部分就在ScheduledThreadPoolExecutor 的run方法里面了,其实关于具体的实现相信没有看过源码也很容易想到,周期执行就是执行完后不做任何操作嘛,对于周期执行,那么就会在执行完成后,再将这个任务重新放入任务队列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值