java线程池 延迟执行和定时任务的实现原理

我们先来看看java线程池实现定时任务的一个简单实现demo

 

我们执行了一个 :延迟1秒钟开始执行并且每隔1秒钟执行一次的定时任务

scheduleAtFixedRate方法第一个参数传入一个lamda表达式的Runnable对象,输出当前执行该任务的线程的名称,和执行时距离开始时间的一个时间差

public class ScheduleTask {  
       
    public static void main(String[] args) { 
        //当前时间 
        long start = System.currentTimeMillis();
        //创建一个单线程的线程池
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();  
        // 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间  
        service.scheduleAtFixedRate(() ->   System.out.println(Thread.currentThread().getName() + "  " +  (System.currentTimeMillis() - start)),
                     1, 1, TimeUnit.SECONDS);  
    }  
}

执行结果:

pool-1-thread-1  1022
pool-1-thread-1  2021
pool-1-thread-1  3021
pool-1-thread-1  4021
pool-1-thread-1  5021
pool-1-thread-1  6021
pool-1-thread-1  7021
pool-1-thread-1  8023
pool-1-thread-1  9020
pool-1-thread-1  10020
pool-1-thread-1  11020
pool-1-thread-1  12020
pool-1-thread-1  13020
pool-1-thread-1  14020
pool-1-thread-1  15020

下面我们根据上面这个demo的执行流程去源码里看看到底是怎么实现的

首先我们利用Executors.newSingleThreadScheduledExecutor()这个方法来生成了一个ScheduledExecutorService

ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
        //调用父类ThreadPoolExecutor的构造方法
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
}

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
}

我们看到返回的对象是一个DelegatedScheduledExecutorService实例,它包装了一个ScheduledThreadPoolExecutor实例

而ScheduledThreadPoolExecutor构造方法调用了父类ThreadPoolExecutor的构造方法

ThreadPoolExecutor是实现线程池的一个核心类,如果对这个类不了解,建议先去了解一下这个类的实现原理,以后我也会写篇博客总结一下,本文不进行赘述

 

我们看到ScheduledThreadPoolExecutor构造方法中传入父类构造方法的BlockingQueue 参数是new DelayedWorkQueue();

这是它能够实现延迟和定时执行的关键,后面会讲述

 

现在我们来看看执行方法

long start = System.currentTimeMillis();
service.scheduleAtFixedRate(() -> System.out.println(Thread.currentThread().getName() + "  " +  (System.currentTimeMillis() - start)),//执行的Runnable任务
                    1, //延迟执行时间
                    1, //定时执行周期
                    TimeUnit.SECONDS); //单位秒 
    } 

调用ScheduledThreadPoolExecutor.scheduleAtFixedRate方法

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (period <= 0L)
            throw new IllegalArgumentException();
        //生成一个task
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),//开始执行任务延迟时间
                                          unit.toNanos(period),//定时任务执行间隔
                                          sequencer.getAndIncrement());
        //装饰一下,这里其实就是返回了sft本身
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        //执行
        delayedExecute(t);
        return t;
    }

ScheduledFutureTask(Runnable r, V result, long triggerTime,
                            long sequenceNumber) {
            super(r, result);
            this.time = triggerTime;//开始执行任务的时间 纳秒
            this.period = 0;//执行定时任务的周期 纳秒
            this.sequenceNumber = sequenceNumber;
        }

 

延迟执行方法

private void delayedExecute(RunnableScheduledFuture<?> task) {
        if (isShutdown())
            reject(task);
        else {
            //将任务放入构造方法里创建的DelayedWorkQueue中,放入的实际是个ScheduledFutureTask实例
            super.getQueue().add(task);
            if (!canRunInCurrentRunState(task) && remove(task))
                task.cancel(false);
            else
                //执行预处理
                ensurePrestart();
        }
    }

void ensurePrestart() {
        //计算当前线程池中的线程数量,因为我们是第一次调用这里,还没创建线程,这里wc == 0
        int wc = workerCountOf(ctl.get());
        //我们是单线程池,这里corePoolSize == 1
        if (wc < corePoolSize)
            //简单理解为创建了一个Worker线程来对DelayedWorkQueue中的任务进行处理
            addWorker(null, true);
        else if (wc == 0)
            addWorker(null, false);
    }

调用父类的addWorker方法,创建工作线程

private boolean addWorker(Runnable firstTask, boolean core) {
        //省略一些校验
        ...............

        boolean workerStarted = false;
        boolean workerAdded = false;
        //工作线程对象,实现了Runnable接口
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int c = ctl.get();
                    if (isRunning(c) ||
                        (runStateLessThan(c, STOP) && firstTask == null)) {
                        if (t.getState() != Thread.State.NEW)
                            throw new IllegalThreadStateException();
                        //加入到Worker集合中
                        workers.add(w);
                        workerAdded = true;
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    //启动工作线程
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

Worker的run方法里调用了runWorker方法,参数为this

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //从任务队列中获取任务
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    try {
                        //执行之前放入延迟队列的ScheduledFutureTask的run方法
                        task.run();
                        afterExecute(task, null);
                    } catch (Throwable ex) {
                        afterExecute(task, ex);
                        throw ex;
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

这里while循环通过getTask获取需要执行的任务

private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
        for (;;) {
            int c = ctl.get();
            // Check if queue empty only if necessary.
            if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
            //我们只有一个worker,所以这里wc == 1
            int wc = workerCountOf(c);
            // Are workers subject to culling?
            //这里没有设置allowCoreThreadTimeOut并且wc > corePoolSize为false,所以timed == false
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
            try {
                //这里从workQueue(初始化时传入的DelayedWorkQueue实例)获取任务会走take方法,
                //因为前面delayedExecute方法里加入了一个task,所以workQueue里已经有一个任务了
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

来看看DelayedWorkQueue.take方法的实现

public RunnableScheduledFuture<?> take() throws InterruptedException {
            final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();
            try {
                for (;;) {
                    RunnableScheduledFuture<?> first = queue[0];
                    if (first == null)
                        available.await();
                    else {
                        //计算还需要等待的时间
                        long delay = first.getDelay(NANOSECONDS);
                        if (delay <= 0L)
                            //结束循环返回task
                            return finishPoll(first);
                        first = null; // don't retain ref while waiting
                        if (leader != null)
                            available.await();
                        else {
                            Thread thisThread = Thread.currentThread();
                            leader = thisThread;
                            try {
                                //等待delay纳秒的时间
                                available.awaitNanos(delay);
                            } finally {
                                if (leader == thisThread)
                                    leader = null;
                            }
                        }
                    }
                }
            } finally {
                if (leader == null && queue[0] != null)
                    available.signal();
                lock.unlock();
            }
        }


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

可以看到任务的延迟和定时执行是通过available.awaitNanos(delay)方法实现的

而这个delay是通过任务的getDelay(NANOSECONDS)方法获取的

它是用task(ScheduledFutureTask)任务初始化时传入的delay时间 - 系统当前时间获得的

这就是为什么任务可以实现延迟执行

 

最后我们发现take方法是调用finishPoll(first)返回的task

这里主要是因为DelayedWorkQueue里面使用二叉堆来维护task列表,所以取出头元素后会调整一下后面元素的位置,感兴趣的同学可以去看一下。对于有数据结构算法基础的同学应该能很容易理解,如果没有的话建议最好系统学习一下,毕竟也是很重要的一块技能,推荐去看下《算法 第四版》这本书。

 

好了,回到前面runWorker的task.run()方法,这里的task是ScheduledFutureTask实例,所以直接看ScheduledFutureTask.run方法

public void run() {
            //检查状态
            if (!canRunInCurrentRunState(this))
                cancel(false);
            //如果不是定时周期执行任务走这里
            else if (!isPeriodic())
                super.run();
            //其他走这里
            else if (super.runAndReset()) {
                setNextRunTime();
                reExecutePeriodic(outerTask);
            }
        }

这里我们会走runAndReset方法

protected boolean runAndReset() {
        if (state != NEW ||
            !RUNNER.compareAndSet(this, null, Thread.currentThread()))
            return false;
        boolean ran = false;
        int s = state;
        try {
            Callable<V> c = callable;
            if (c != null && s == NEW) {
                try {
                    //方法调用
                    c.call(); // don't set result
                    ran = true;
                } catch (Throwable ex) {
                    setException(ex);
                }
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
        return ran && s == NEW;
    }

c.call(),这里c是一个Callable对象,它是在前面ScheduledFutureTask初始化时包装的一个对象,实际实现是RunnableAdapter

 

可以看到call方法就是调用task的run方法,而这里的task就是我们demo中传入的lamda表达式对象

private static final class RunnableAdapter<T> implements Callable<T> {
        private final Runnable task;
        private final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
        public String toString() {
            return super.toString() + "[Wrapped task = " + task + "]";
        }
    }

到这里,就完成了第一次的task执行,那么后序该怎么实现定时任务呢?

其实相信这里大家也能猜出个大概了

1.首先之前我们获取task的时候从任务队列中取出了一个任务,这时任务队列已经是空的了,所以后面肯定会有一个地方将task再次放入任务队列

2.我们前面分析DelayedWorkQueue.take方法时,了解到代码通过task.getDelay方法来获取等待时间,而task.getDelay通过保存的成员变量time-当前系统时间获得,如果我们不重置这个time属性,那么下次去调用DelayedWorkQueue.take时获取的时间肯定是负的,任务会立马执行,也就起不到定时任务的效果了

 

回到runAndReset执行完后,会进入setNextRunTime方法,这里重置了time,将time加上了定时任务时间间隔period

private void setNextRunTime() {
            long p = period;
            if (p > 0)
                time += p;
            else
                time = triggerTime(-p);
        }

然后是reExecutePeriodic,这里将task再次加入任务队列

void reExecutePeriodic(RunnableScheduledFuture<?> task) {
        if (canRunInCurrentRunState(task)) {
            super.getQueue().add(task);
            if (canRunInCurrentRunState(task) || !remove(task)) {
                ensurePrestart();
                return;
            }
        }
        task.cancel(false);
    }

然后就是一直在runWorker的while循环里获取task和执行task

整个流程就差不多是这样,主要是利用DelayedWorkQueue和ScheduledFutureTask来实现延迟执行和定时任务的效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值