Timer和ScheduledThreadPoolExecutor的区别及源码分析

博客 动态
0 136
优雅殿下
优雅殿下 2022-07-01 23:01:16
悬赏:0 积分 收藏

Timer和ScheduledThreadPoolExecutor的区别及源码分析

Timer

基于单线程系统时间实现的延时、定期任务执行类。具体可以看下面红色标注的代码。

public class Timer {    /**     * The timer task queue.  This data structure is shared with the timer     * thread.  The timer produces tasks, via its various schedule calls,     * and the timer thread consumes, executing timer tasks as appropriate,     * and removing them from the queue when they're obsolete.     */    private final TaskQueue queue = new TaskQueue();    /**     * The timer thread.*/    private final TimerThread thread = new TimerThread(queue);
class TimerThread extends Thread {    /**     * This flag is set to false by the reaper to inform us that there     * are no more live references to our Timer object.  Once this flag     * is true and there are no more tasks in our queue, there is no     * work left for us to do, so we terminate gracefully.  Note that     * this field is protected by queue's monitor!     */    boolean newTasksMayBeScheduled = true;    /**     * Our Timer's queue.  We store this reference in preference to     * a reference to the Timer so the reference graph remains acyclic.     * Otherwise, the Timer would never be garbage-collected and this     * thread would never go away.     */    private TaskQueue queue;    TimerThread(TaskQueue queue) {        this.queue = queue;    }    public void run() {        try {            mainLoop();        } finally {            // Someone killed this Thread, behave as if Timer cancelled            synchronized(queue) {                newTasksMayBeScheduled = false;                queue.clear();  // Eliminate obsolete references            }        }    }    /**     * The main timer loop.  (See class comment.)     */    private void mainLoop() {        while (true) {            try {                TimerTask task;                boolean taskFired;                synchronized(queue) {                    // Wait for queue to become non-empty                    while (queue.isEmpty() && newTasksMayBeScheduled)                        queue.wait();                    if (queue.isEmpty())                        break; // Queue is empty and will forever remain; die                    // Queue nonempty; look at first evt and do the right thing                    long currentTime, executionTime;                    task = queue.getMin();                    synchronized(task.lock) {                        if (task.state == TimerTask.CANCELLED) {                            queue.removeMin();                            continue;  // No action required, poll queue again                        }                        currentTime = System.currentTimeMillis();                        executionTime = task.nextExecutionTime;                        if (taskFired = (executionTime<=currentTime)) {                            if (task.period == 0) { // Non-repeating, remove                                queue.removeMin();                                task.state = TimerTask.EXECUTED;                            } else { // Repeating task, reschedule                                queue.rescheduleMin(                                  task.period<0 ? currentTime   - task.period                                                : executionTime + task.period);                            }                        }                    }                    if (!taskFired) // Task hasn't yet fired; wait                        queue.wait(executionTime - currentTime);                }                if (taskFired)  // Task fired; run it, holding no locks                    task.run();            } catch(InterruptedException e) {            }        }    }}

 Timer延时、定时任务的实现采用单线程,在主循环(mainLoop)中循环遍历任务队列(TaskQueue),如果执行时间小于等于当前系统时间则执行任务,否则继续等待(执行时间-当前时间)。

ScheduledThreadPoolExecutor

基于多线程、JVM时间实现的延时、定期任务执行类。具体可以看下面红色标注的代码。

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

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 <= 0)                            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 long getDelay(TimeUnit unit) {            return unit.convert(time - now(), NANOSECONDS);        }
    /**     * Returns current nanosecond time.     */    final long now() {        return System.nanoTime();    }

ThreadPoolExecutor执行流程

submit(task)->execute(task)
->1.当前线程数<核心线程数: addWorker(核心工作者线程)->runWorker-> 循环【getTask(workQueue.take)->task.run】
->2.当前线程数>=核心线程数:排队任务成功:task add to workQueue(BlockingQueue)->addWorker(非核心工作者线程)......
->3.当前线程数>=核心线程数:排队任务失败:尝试添加新线程执行任务 addWorker(非核心工作者线程)......

ScheduledThreadPoolExecutor执行延时、定期任务,核心代码就在runWorker,循环获取任务队列中的任务然后执行,在获取任务的时候如果任务的执行时间没到,则进行等待。延时时间的计算都是基于System.nanoTime(),即JVM时间。

 

优缺点:

1.Timer单线程,执行周期任务时,一次出错,则TimerThread线程终止, 所有任务将无法执行。而且任务的执行时间可能会影响周期的准确性。

2.Timer基于系统时间,系统时间的修改会影响任务的执行。在以系统时间为准的场景中(public void schedule(TimerTask task, Date time))使用非常合适,使用周期性任务则受到极大影响,因为时间间隔被破坏!

3.ScheduledThreadPoolExecutor多线程,任务的执行不会相互影响,且能保证执行时间间隔的准确性。

4.ScheduledThreadPoolExecutor基于JVM时间,该时间本身无任何意义,仅用来计算时间间隔,不受系统时间影响。所以用来计算周期间隔特别合适,而且单位是纳秒更加精确。因此延时任务、周期任务采用它比Timer更加靠谱!

 

总结:

Timer的使用场景,仅在基于系统时间为准的场景中非常合适(依赖当前系统时间进行判断任务的执行)。

ScheduledThreadPoolExecutor的使用场景则更为广泛,对延时任务、周期任务使用此类更靠谱(依赖时间间隔(JVM时间差值计算得到)进行判断任务的执行)。基于系统时间执行的任务则无法精确(因为系统时间可以随时调整)!

 

posted @ 2022-07-01 22:23 追极 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2018 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员