ScheduledThreadPoolExecutor 定时任务理解

ScheduledThreadPoolExecutor 定时轮询任务实现

一、使用入口

可以在应用初始化时就加载好该线程池。

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(8);
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello world");
            }
}, 1, 3, TimeUnit.SECONDS);//可以配置轮询任务的间隔时间

二、使用原理

ScheduledThreadPoolExecutor是基于线程池实现的,本人理解为初始化一个轮询任务线程池后,将任务放到线程池中执行,某个线程未到执行时间前处于阻塞状态,到了任务执行时间后变为运行状态。如果是轮询任务,则在本次执行完后,计算下一次需要执行的时间,插入到任务队列中对应的位置,等待下一次执行。

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();
        //将任务进行一层封装,最后得到一个ScheduledFutureTask对象
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(period));
        //处理sft这个对象,源码中其实未进行处理
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        //提交给线程池执行
        delayedExecute(t);
        return 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
                //因为这里的定时任务是直接放到任务队列中,所以需要保证已经有worker启动了
                ensurePrestart();
        }
}

void ensurePrestart() {
        int wc = workerCountOf(ctl.get());
        //如果worker的数量小于corePoolSize,那就启动一个worker,用来消费任务队列的任务
        if (wc < corePoolSize)
            addWorker(null, true);
        //worker的数量为0也直接启动一个worker
        else if (wc == 0)
            addWorker(null, false);
}

 private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // 检查任务队列是否为空
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                //检查当前线程池中的线程是否超过最大线程数
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                //否则,由于workerCount更改,CAS失败;重试内部循环
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            final ReentrantLock mainLock = this.mainLock;
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                mainLock.lock();
                try {
                    int c = ctl.get();
                    int rs = runStateOf(c);

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // 检查线程是否可执行
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    //执行该任务
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

到这里,任务已经放到线程池中去执行了,如果还需要延迟任务的执行时间,那么需要通过将任务队列变成延迟队列,worker不会马上获取到任务队列中的任务了。只有任务的时间到了,worker线程才能从延迟队列中获取到任务并执行。在ScheduledThreadPoolExecutor中,定义了DelayedWorkQueue类来实现延迟队列。DelayedWorkQueue内部使用了最小堆的数据结构,当任务插入到队列中时,会根据执行的时间自动调整在堆中的位置,最后执行时间最近的那个会放在堆顶。当worker要去队列获取任务时,如果堆顶的执行时间还没到,那么worker就会阻塞一定时间后才能获取到那个任务,这样就实现了任务的延迟执行。

周期任务的实现是在ScheduledFutureTask中,处理完本次任务后,会计算下一次的执行时间,然后重新放到任务队列中等待下一次执行。

public void run() {
            //先判断任务是否周期执行
            boolean periodic = isPeriodic();
            //判断是否能执行任务
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            //判断是否周期性任务
            else if (!periodic)
                //不是的话执行执行run方法
                ScheduledFutureTask.super.run();
            else if (ScheduledFutureTask.super.runAndReset()) {
                //如果是周期性任务,那就设置下一次的执行时间
                setNextRunTime();
                //重新将任务放到队列中,然后等待下一次执行
                reExecutePeriodic(outerTask);
            }
}
private void setNextRunTime() {
            //根据peroid的正负来判断下一次执行时间的计算策略
            //和timer的下一次执行时间计算策略有点像
            long p = period;
            if (p > 0)
                time += p;
            else
                time = triggerTime(-p);
}
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
        //先判断是否可以在当前状态下执行
        if (canRunInCurrentRunState(true)) {
            //重新加任务放到任务队列中
            super.getQueue().add(task);
            if (!canRunInCurrentRunState(true) && remove(task))
                task.cancel(false);
            else
                ensurePrestart();
        }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值