ScheduledThreadPoolExecutor除了具有ThreadPoolExecutor的所有功能外,还可以延迟执行任务或者周期性的执 行某个任务。scheduleWithFixedDelay和scheduleAtFixedRate就是用来完成这个功能的。平常使用 scheduleAtFixedRate这个方法时并没有多想,但是这几天在实现一个功能的时候,需要考虑scheduleAtFixedRate所执行 的task是否会影响任务的周期性,比如scheduleAtFixedRate(command,5,10,TimeUnit.SECONDS),那么 这个command的执行会不会影响这个10秒的周期性。因此特意仔细看了下ScheduledThreadPoolExecutor的源代码,这里记录 一下,以便以后查看。

scheduleAtFixedRate有两个时间参数,initialDelay和period,对应该方法的两个主要功能,即延迟运行任务和周期性执行任务。

  1. public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
  2. long initialDelay,
  3. long period,
  4. TimeUnit unit) {
  5. if (command == null || unit == null)
  6. throw new NullPointerException();
  7. if (period <= 0)
  8. throw new IllegalArgumentException();
  9. RunnableScheduledFuture<?> t = decorateTask(command,
  10. new ScheduledFutureTask<Object>(command,
  11. null,
  12. triggerTime(initialDelay, unit),
  13. unit.toNanos(period)));
  14. delayedExecute(t);
  15. return t;
  16. }
  17. /**
  18. * Specialized variant of ThreadPoolExecutor.execute for delayed tasks.
  19. */
  20. private void delayedExecute(Runnable command) {
  21. if (isShutdown()) {
  22. reject(command);
  23. return;
  24. }
  25. // Prestart a thread if necessary. We cannot prestart it
  26. // running the task because the task (probably) shouldn't be
  27. // run yet, so thread will just idle until delay elapses.
  28. if (getPoolSize() < getCorePoolSize())
  29. prestartCoreThread();
  30. super.getQueue().add(command);
  31. }

首先创建一个ScheduledFutureTask,然后通过delayedExecute执行这个task。在delayedExecute中,首先 预先启动一个线程,这里要注意的是这个这里用来启动一个新线程的firstTask参数是null,所以新启动的线程是idle状态的,然后把这个 task加入到workQueue。ScheduledThreadPoolExecutor里的workQueue用的是 DelayedWorkQueue,这个DelayedWorkQueue就是实现delay的关键。DelayedWorkQueue内部使用的是 DelayQueue,DelayQueue实现task delay的关键就在于其Offer(E e)和Take.下面,通过分析这两个方法和结合ThreadPoolExecutor的运行原理来说明delay操作是如何实现的

  1. public boolean offer(E e) {
  2. final ReentrantLock lock = this.lock;
  3. lock.lock();
  4. try {
  5. E first = q.peek();
  6. q.offer(e);
  7. if (first == null || e.compareTo(first) < 0)
  8. available.signalAll();
  9. return true;
  10. } finally {
  11. lock.unlock();
  12. }
  13. }
  14. public E take() throws InterruptedException {
  15. final ReentrantLock lock = this.lock;
  16. lock.lockInterruptibly();
  17. try {
  18. for (;;) {
  19. E first = q.peek();
  20. if (first == null) {
  21. available.await();
  22. } else {
  23. long delay =  first.getDelay(TimeUnit.NANOSECONDS);
  24. if (delay > 0) {
  25. long tl = available.awaitNanos(delay);
  26. } else {
  27. E x = q.poll();
  28. assert x != null;
  29. if (q.size() != 0)
  30. available.signalAll(); // wake up other takers
  31. return x;
  32. }
  33. }
  34. }
  35. } finally {
  36. lock.unlock();
  37. }
  38. }

ScheduledThreadPoolExecutor执行task是通过工作线程Work来承担的,Work的Run方法如下:

  1. public void run() {
  2. try {
  3. Runnable task = firstTask;
  4. firstTask = null;
  5. while (task != null || (task = getTask()) != null) {
  6. runTask(task);
  7. task = null;
  8. }
  9. } finally {
  10. workerDone(this);
  11. }
  12. }

因为前面在delayedExecute方法里面创建work线程的firstTask参数为null,所以就通过getTask去从workQueue 里面获取task,getTask在正常情况下(即线程池没有关闭,线程数量没有超过corePoolSize等)是通过 workQueue.take()从workQueue里获取任务。根据上面的贴出来的take方法的代码,如果queue是空的,则take方法会阻塞 住,直到有新task被add进来。而在上面的delayedExecute方法的最后,会把创建的scheduledFutureTask加入到 workQueue,这样take方法中的available.await()就被唤醒;在take方法里面,如果workQueue不为空,则执行 task.getDelay()方法获取task的delay

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

这里的time是通过两个方法把initialDelay变成一个triggerTime

  1. /**
  2. * Returns the trigger time of a delayed action.
  3. */
  4. private long triggerTime(long delay, TimeUnit unit) {
  5. return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
  6. }
  7. /**
  8. * Returns the trigger time of a delayed action.
  9. */
  10. long triggerTime(long delay) {
  11. return now() +
  12. ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
  13. }

注意看这个方法,这里返回的delay不是固定不变的,从task被放入workQueue起,不同的时间调用getDelay方法会得出不同的 delay。如果放入workQueue的task的initialDelay是5秒,那么根据take方法的代码,如果在放入workQueue5秒 后,就可以从delayQueue中拿到5秒前put进去的task,这样就实现了delay的功能。

在本文的最前面提到scheduleAtFixedRate能够周期性地执行一项任务,那么这个是如何实现的呢?在 scheduleAtFixedRate方法里创建了一个ScheduledFutureTask,这个ScheduledFutureTask包装了 command,最后周期性执行的是ScheduledFutureTask的run方法。

  1. private void runPeriodic() {
  2. boolean ok = ScheduledFutureTask.super.runAndReset();
  3. boolean down = isShutdown();
  4. // Reschedule if not cancelled and not shutdown or policy allows
  5. if (ok && (!down ||
  6. (getContinueExistingPeriodicTasksAfterShutdownPolicy() &&
  7. !isStopped()))) {
  8. long p = period;
  9. if (p > 0)
  10. time += p;
  11. else
  12. time = triggerTime(-p);
  13. ScheduledThreadPoolExecutor.super.getQueue().add(this);
  14. }
  15. // This might have been the final executed delayed
  16. // task.  Wake up threads to check.
  17. else if (down)
  18. interruptIdleWorkers();
  19. }
  20. /**
  21. * Overrides FutureTask version so as to reset/requeue if periodic.
  22. */
  23. public void run() {
  24. if (isPeriodic())
  25. runPeriodic();
  26. else
  27. ScheduledFutureTask.super.run();
  28. }

由上面的代码可以看出,scheduleAtFixedRate(command,5,10,TimeUnit.SECONDS)这个方法的周期性会受 command的影响,如果command方法的执行时间是10秒,那么执行command的周期其实是20秒,即 scheduleAtFixedRate这个方法要等一个完整的command方法执行完成后才继续周期性地执行command方法,其实这样的设计也是 符合常理的。

以上就是对ScheduledThreadPoolExecutor的一点小理解。

来自:http://olylakers.iteye.com/blog/1218243

ScheduledThreadPoolExecutor的scheduleAtFixedRate方法探究的更多相关文章

  1. ScheduledExecutorService中scheduleAtFixedRate方法与scheduleWithFixedDelay方法的区别

    ScheduledExecutorService中scheduleAtFixedRate方法与scheduleWithFixedDelay方法的区别 ScheduledThreadPoolExecut ...

  2. Android View各种尺寸位置相关的方法探究

    Android View各种尺寸位置相关的方法探究 本来想做一个View间的碰撞检测之类的. 动手做了才发现不是想象的那么简单. 首先,写好了碰撞检测的工具类如下: package com.mengd ...

  3. 简单理解java中timer的schedule和scheduleAtFixedRate方法的区别

    timer的schedule和scheduleAtFixedRate方法一般情况下是没什么区别的,只在某个情况出现时会有区别--当前任务没有来得及完成下次任务又交到手上. 我们来举个例子: 暑假到了老 ...

  4. pandas重置索引的几种方法探究

    pandas重置索引的几种方法探究 reset_index() reindex() set_index() 函数名字看起来非常有趣吧! 不仅如此. 需要探究. http://nbviewer.jupy ...

  5. 关于scheduleAtFixedRate方法与scheduleWithFixedDelay的使用

    一.scheduleAtFixedRate方法 该方法是ScheduledExecutorService中的方法,用来实现周期性执行给定的任务,public ScheduledFuture<?& ...

  6. Timer的schedule和scheduleAtFixedRate方法的区别解析(转)

    在java中,Timer类主要用于定时性.周期性任务 的触发,这个类中有两个方法比较难理解,那就是schedule和scheduleAtFixedRate方法,在这里就用实例分析一下 (1)sched ...

  7. Timer的schedule和scheduleAtFixedRate方法的区别解析

    在java中,Timer类主要用于定时性.周期性任务 的触发,这个类中有两个方法比较难理解,那就是schedule和scheduleAtFixedRate方法,在这里就用实例分析一下 (1)sched ...

  8. ScheduledThreadPoolExecutor之remove方法

    之前用定时任务的线程池,设置了个任务,但是突然今天产品说,某些个操作需要中断某些任务(如果任务还没有执行),使其不能再到点执行了.于是查了API果然有这样一个方法. 一看API,需要移除的是一个Run ...

  9. java设计模式之工厂方法探究

    简单工厂 + 工厂方法 + 抽象工厂       看了十几篇博客,每篇基本上都能有个自己的解释,我汇总这些内容,重新梳理整理了一番,以形成自己的理解.       简单工厂模式其实不算23种设计模式之 ...

随机推荐

  1. codevs1081 线段树练习 2<区间修改>

    1081 线段树练习 2 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 大师 Master   题目描述 Description 给你N个数,有两种操作 1:给区间[a,b]的所有 ...

  2. [ARC055D]隠された等差数列

    题意:对一个等差数列$a_i=A+Bi(0\leq i\leq n-1)$和非负整数$x$,把$a_i$的$10^x$位拿出来可以写成一个字符集为$0\cdots9$的字符串,现在给定这个字符串$d_ ...

  3. 20162327WJH《程序设计与数据结构》课程总结

    20162327<程序设计与数据结构>课程总结 一.每周作业链接汇总 预备作业1:第一篇博客主要谈论了对本学期学习的展望,树立了一个目标. 预备作业2:简单的谈了谈自己的优势和一些成功的案 ...

  4. 最短路:我的理解--Dijkstra算法

    最短路径:Dijkstra算法 用来计算从一个点到其他所有点的最短路径的算法,是一种单源最短路径算法.也就是说,只能计算起点只有一个的情况. Dijkstra的时间复杂度是O (N2),它不能处理存在 ...

  5. [转]Intent和PendingIntent的区别

    intent英文意思是意图,pending表示即将发生或来临的事情. PendingIntent这个类用于处理即将发生的事情.比如在通知Notification中用于跳转页面,但不是马上跳转. Int ...

  6. MYSQL学习笔记 (五)常用的聚合函数

    1.COUNT(e1) 语法:COUNT(e1) 参数:e1为一个表达式,可以是任意的数据类型 返回:返回数值型数据 作用:返回e1指定列不为空的记录总数 例子: 1)单独使用

  7. ffmpeg的IO操作

    ffmpeg 可以通过IO操作将数据读取和存储在文件或网络中 作为数据的读取和写入地址,数据被存放在file,http, ffmpeg 不仅可以编解常用的音视频格式,还可以将数据导入/导出到各种媒介中 ...

  8. 图解linux内核编译框架

    内核是如何编译成的 -知其然而不知其所以然 (第一篇) 转载:http://blog.chinaunix.net/uid-28236237-id-3840137.html Linux内核有分门别类的目 ...

  9. 《Unity3D大风暴之入门篇(海量教学视频版)》

    <Unity3D大风暴之入门篇(海量教学视频版)> 基本信息 作者: 智画互动开发团队 出版社:电子工业出版社 ISBN:9787121222429 上架时间:2014-1-13 出版日期 ...

  10. LTE试题

    D 如果出现eNB的告警1018007“小区退服,光口不可用”,不可能是以下哪种原因造成的?(          ) 基带板上Ir接口光模块损坏 基带板上Ir接口光模块被拔出 基带板上Ir接口光模块型 ...