Java定时任务之ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor是JDK5提供的可执行定时任务的一个工具类,可以在多线程环境下延迟执行任务或者定期执行任务;和Timer类似,它也提供了三种定时模式:

  1. 延迟执行任务
  2. 固定延迟的定期执行(fixed delay)
  3. 按照固定的周期执行(fixed rate)

延迟执行任务

任务将按照给定的时间延迟delay后开始执行;对应的方法如下:

schedule(Runnable command, long delay, TimeUnit unit)  schedule(Callable<V> callable, long delay, TimeUnit unit)  

下面我们通过一个例子检验下结果是否正确:

ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); PrintUtil.print("start schedule a task."); executor.schedule(new Runnable() { @Override public void run() { PrintUtil.print("task is running."); }}, 5, TimeUnit.SECONDS);

我们计划了一个5秒钟后执行的任务,通过打印结果可以看到确实按照给定时间执行了:

15:44:07--start schedule a task.15:44:12--task is running.

固定延迟的定期执行

任务第一次按照给定的初始延迟initialDelay执行,后续每一次执行的时间为上一次任务的结束时间加上给定的period后执行;对应的方法如下:

scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

同样我们通过一个例子检验下结果是否正确:

ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); PrintUtil.print("start schedule a task."); executor.scheduleWithFixedDelay(new Runnable() { @Override public void run() { PrintUtil.print("task is running."); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } }}, 0, 5, TimeUnit.SECONDS);

我们计划了一个定期(每5秒钟)延迟执行的任务,第一次任务立即执行,每次任务执行时长2秒钟,通过打印的日志我们可以看到每次任务开始执行的时间为:上次任务结束时间 5秒钟:

15:55:16--start schedule a task.15:55:16--task is running.15:55:18--task is finished.15:55:23--task is running.15:55:25--task is finished.15:55:30--task is running.15:55:32--task is finished.

按照固定的周期执行

任务第一次按照给定的初始延迟initialDelay执行,后续每一次执行的时间为固定的时间间隔period,如果线程池中工作线程不够则任务顺延执行;对应的方法如下:

scheduleWithFixedDelay(Runnable command, long initialDelay, long period, TimeUnit unit)

同样我们通过一个例子检验下结果是否正确:

ScheduledExecutorService executor = Executors.newScheduledThreadPool(10); PrintUtil.print("start schedule a task."); executor.scheduleAtFixedRate(new Runnable() { @Override public void run() { PrintUtil.print("task is running."); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } PrintUtil.print("task is finished."); }}, 0, 5, TimeUnit.SECONDS);

我们创建了一个核心线程池为10的ScheduledThreadPoolExecutor,并计划了一个定期(每5秒钟)执行一次的任务,过打印的日志我们可以看到每次任务开始执行的时间为:上次任务开始时间 5秒钟:

16:02:43--start schedule a task.16:02:43--task is running.16:02:45--task is finished.16:02:48--task is running.16:02:50--task is finished.

上面的例子都是计划了一个任务,如果是有多个定时任务同时执行会怎么样呢?

如果线程足够并且CPU资源足够,那就会同时执行,如果线程或者CPU资源不够那只能排队执行了。有兴趣的话可以克隆文末的测试代码,里面提供了一些测试的例子。

底层原理

ScheduledThreadPoolExecutor是如何保证我们计划的任务都是按照正确的时间点执行的呢?

其内部实现了一个阻塞队列DelayedWorkQueue,所有的任务都会放到这个队列里。这个阻塞队列内部通过一个数组来保存这些任务,并且基于最小堆排序,按照每个任务的下次执行时间进行排序,这样就保证了执行线程拿到的这个队列中的第一个元素就是最接近当前时间执行的任务了。

相关的源码如下:

// 保存定时任务的队列private RunnableScheduledFuture<?>[] queue = new RunnableScheduledFuture<?>[INITIAL_CAPACITY];// 最小堆排序相关的方法private void siftUp(int k, RunnableScheduledFuture<?> key)private void siftDown(int k, RunnableScheduledFuture<?> key)


那时间上是如何保证的呢?

DelayedWorkQueue重写了take和poll方法,利用了AQS的ConditionObject机制使当前线程休眠,等时间到了再唤醒线程去拿第一个任务。

关于AQS和ConditonObject的介绍,可以参考下文末的链接。

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

优点

作为对JDK1.3推出的Timer的替代,ScheduledThreadPoolExecutor有如下优点:

  1. 它支持多个定时任务同时执行,而Timer是单线程执行的
  2. 它通过System.nanoTime()保证了任务执行时间不受操作系统时间变化的影响
  3. 一个定时任务抛出异常,其他定时任务不受影响,而Timer却不支持这一点

Demo代码

src/main/java/net/weichitech/util/ScheduledThreadPoolExecutorDemo.java · 小西学编程/java-learning – Gitee.com

参考文章

Java定时任务之Timer原理解析

JAVA并发之ReentrantLock原理解析

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

(0)
上一篇 2022年12月11日 上午10:36
下一篇 2022年12月11日 上午10:50

相关推荐

  • 光大银行包头分行张毅强

    光大银行包头分行张毅强: 创新金融助力城市发展 张毅强是光大银行包头分行的一位资深的金融从业者,也是该分行的一位重要领导。他在过去的几十年中,一直在推动银行的创新和发展,为城市的经…

    科研百科 2024年10月16日
    23
  • 科研项目实施体系怎么写

    科研项目实施体系怎么写 科研项目实施体系是科研项目的重要组成部分,其质量直接影响着科研项目的成功与否。因此,制定合理的科研项目实施体系对于提高科研项目实施效率、保证科研项目质量具有…

    科研百科 2025年4月14日
    11
  • 科研项目管理手册,科研合同、行为规范、协议和知情同意原则(科研项目合同管理办法)

    科研合同和伦理准则并不是最近才出现的概念。事实上它们已经存在多年,用途多种多样。也许它们曾被冠以不同的名字,和今天相比使用也不太正式,但它们实实在在地存在着。卢茨在其关于民族志研究…

    科研百科 2024年1月16日
    153
  • 项目跟踪调查管理系统

    项目跟踪调查管理系统 随着现代商业的发展,项目开发和管理已经成为了一个越来越复杂和重要的任务。传统的手动项目跟踪和调查方法已经无法满足现代项目的需求,因此开发一款高效的项目跟踪调查…

    科研百科 2025年6月24日
    0
  • 软件工程与项目管理解析

    软件工程与项目管理解析 随着信息技术的不断发展,软件工程和项目管理成为了现代软件开发和项目管理中不可或缺的一部分。本文将解析软件工程和项目管理的核心概念、方法和工具,帮助读者更好地…

    科研百科 2024年9月26日
    3
  • 以色列最近光学科研项目

    以色列最近光学科研项目 近年来,以色列在光学领域的研究取得了重要进展,尤其是在量子光学和光学通信方面。最近,以色列进行了一项备受瞩目的光学科研项目,旨在探索光在材料中的相互作用。 …

    科研百科 2025年4月1日
    2
  • 如何获取国外科研项目

    如何获取国外科研项目 随着全球化的发展,获取国外科研项目已经成为一项非常重要的任务。以下是一些获取国外科研项目的方法: 1. 联系导师或教授:如果你正在申请一个国外的科研项目,你可…

    科研百科 2025年4月5日
    3
  • 文件权限的管理(文件权限管理软件)

    文件权限管理软件 随着计算机的普及,文件权限管理已经成为了一个越来越重要的问题。在Windows操作系统中,文件权限管理可以通过管理员账号来执行,但是在某些情况下,普通用户可能会遇…

    科研百科 2024年8月30日
    3
  • 项目管理系统的思维导图

    项目管理系统的思维导图 项目管理系统是一个重要的工具,可以帮助项目经理和团队成员更好地协调和管理项目。本文将介绍项目管理系统的思维导图,包括项目管理系统的基本组成部分、使用项目管理…

    科研百科 2025年7月13日
    1
  • 企业流程管理(企业管理流程)

    #头条创作挑战赛#在企业的现实工作中,我们谈到“流程”两个字的时候,其实存在有两个方面的理解,即“审批流程”与“业务流程”;在此,我们主要探讨的是“业务流程”,而不是“审批流程”。…

    科研百科 2022年10月1日
    341